From a9ead73c49b7f3bec83e5ce273e2674ab10c1a1e Mon Sep 17 00:00:00 2001 From: Ro Arepally <30358289+RohanArepally@users.noreply.github.com> Date: Wed, 21 Aug 2024 19:26:47 -0400 Subject: [PATCH 001/282] Add option for additional context to Sanic adapter handler (#1135) --- slack_bolt/adapter/sanic/async_handler.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/slack_bolt/adapter/sanic/async_handler.py b/slack_bolt/adapter/sanic/async_handler.py index 6bbf14407..004de61eb 100644 --- a/slack_bolt/adapter/sanic/async_handler.py +++ b/slack_bolt/adapter/sanic/async_handler.py @@ -1,4 +1,5 @@ from datetime import datetime +from typing import Any, Dict, Optional from sanic.request import Request from sanic.response import HTTPResponse @@ -8,13 +9,19 @@ from slack_bolt.oauth.async_oauth_flow import AsyncOAuthFlow -def to_async_bolt_request(req: Request) -> AsyncBoltRequest: - return AsyncBoltRequest( +def to_async_bolt_request(req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> AsyncBoltRequest: + request = AsyncBoltRequest( body=req.body.decode("utf-8"), query=req.query_string, headers=req.headers, # type: ignore[arg-type] ) + if addition_context_properties is not None: + for k, v in addition_context_properties.items(): + request.context[k] = v + + return request + def to_sanic_response(bolt_resp: BoltResponse) -> HTTPResponse: resp = HTTPResponse( @@ -42,19 +49,19 @@ class AsyncSlackRequestHandler: def __init__(self, app: AsyncApp): self.app = app - async def handle(self, req: Request) -> HTTPResponse: + async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> HTTPResponse: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: AsyncOAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: - bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req)) + bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) elif req.path == oauth_flow.redirect_uri_path: - bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req)) + bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) elif req.method == "POST": - bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req)) + bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) return HTTPResponse( From 6f4854b61e49938850a729b8bda2dfd1820b0eee Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 21 Aug 2024 23:27:55 +0000 Subject: [PATCH 002/282] chore: naming of is_coroutine_function (#1134) --- slack_bolt/app/async_app.py | 8 ++++---- slack_bolt/middleware/async_custom_middleware.py | 4 ++-- slack_bolt/util/utils.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 819ab1879..8f66e3ba3 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -25,7 +25,7 @@ AsyncMessageListenerMatches, ) from slack_bolt.oauth.async_internals import select_consistent_installation_store -from slack_bolt.util.utils import get_name_for_callable, is_coroutine_function +from slack_bolt.util.utils import get_name_for_callable, is_callable_coroutine from slack_bolt.workflows.step.async_step import ( AsyncWorkflowStep, AsyncWorkflowStepBuilder, @@ -786,7 +786,7 @@ async def custom_error_handler(error, body, logger): func: The function that is supposed to be executed when getting an unhandled error in Bolt app. """ - if not is_coroutine_function(func): + if not is_callable_coroutine(func): name = get_name_for_callable(func) raise BoltError(error_listener_function_must_be_coro_func(name)) self._async_listener_runner.listener_error_handler = AsyncCustomListenerErrorHandler( @@ -1418,7 +1418,7 @@ def _register_listener( value_to_return = functions[0] for func in functions: - if not is_coroutine_function(func): + if not is_callable_coroutine(func): name = get_name_for_callable(func) raise BoltError(error_listener_function_must_be_coro_func(name)) @@ -1430,7 +1430,7 @@ def _register_listener( for m in middleware or []: if isinstance(m, AsyncMiddleware): listener_middleware.append(m) - elif callable(m) and is_coroutine_function(m): + elif callable(m) and is_callable_coroutine(m): listener_middleware.append(AsyncCustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) diff --git a/slack_bolt/middleware/async_custom_middleware.py b/slack_bolt/middleware/async_custom_middleware.py index 77bfb2687..18856a3b2 100644 --- a/slack_bolt/middleware/async_custom_middleware.py +++ b/slack_bolt/middleware/async_custom_middleware.py @@ -6,7 +6,7 @@ from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse from .async_middleware import AsyncMiddleware -from slack_bolt.util.utils import get_name_for_callable, get_arg_names_of_callable, is_coroutine_function +from slack_bolt.util.utils import get_name_for_callable, get_arg_names_of_callable, is_callable_coroutine class AsyncCustomMiddleware(AsyncMiddleware): @@ -23,7 +23,7 @@ def __init__( base_logger: Optional[Logger] = None, ): self.app_name = app_name - if is_coroutine_function(func): + if is_callable_coroutine(func): self.func = func else: raise ValueError("Async middleware function must be an async function") diff --git a/slack_bolt/util/utils.py b/slack_bolt/util/utils.py index a5bcdbe5f..738b6bf03 100644 --- a/slack_bolt/util/utils.py +++ b/slack_bolt/util/utils.py @@ -90,7 +90,7 @@ def get_arg_names_of_callable(func: Callable) -> List[str]: return inspect.getfullargspec(inspect.unwrap(func)).args -def is_coroutine_function(func: Optional[Any]) -> bool: +def is_callable_coroutine(func: Optional[Any]) -> bool: return func is not None and ( inspect.iscoroutinefunction(func) or (hasattr(func, "__call__") and inspect.iscoroutinefunction(func.__call__)) ) From 3aa9c30f2f49d5db9bef2813bb0c26f2d5ef16b2 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 23 Aug 2024 14:31:18 +0000 Subject: [PATCH 003/282] version 1.20.1 (#1137) --- .../adapter/asgi/aiohttp/index.html | 6 +- .../adapter/asgi/async_handler.html | 6 +- .../slack_bolt/adapter/asgi/base_handler.html | 14 +-- .../adapter/asgi/builtin/index.html | 8 +- .../slack_bolt/adapter/asgi/http_request.html | 10 +- .../slack_bolt/adapter/asgi/index.html | 8 +- .../adapter/aws_lambda/chalice_handler.html | 10 +- .../chalice_lazy_listener_runner.html | 2 +- .../adapter/aws_lambda/handler.html | 2 +- .../slack_bolt/adapter/aws_lambda/index.html | 2 +- .../slack_bolt/adapter/bottle/handler.html | 4 +- .../slack_bolt/adapter/bottle/index.html | 4 +- .../slack_bolt/adapter/cherrypy/handler.html | 4 +- .../slack_bolt/adapter/cherrypy/index.html | 4 +- .../slack_bolt/adapter/django/handler.html | 4 +- .../slack_bolt/adapter/django/index.html | 4 +- .../adapter/falcon/async_resource.html | 2 +- .../slack_bolt/adapter/falcon/index.html | 2 +- .../slack_bolt/adapter/falcon/resource.html | 2 +- .../adapter/fastapi/async_handler.html | 2 +- .../slack_bolt/adapter/fastapi/index.html | 2 +- .../slack_bolt/adapter/flask/handler.html | 4 +- .../slack_bolt/adapter/flask/index.html | 4 +- .../google_cloud_functions/handler.html | 4 +- .../adapter/google_cloud_functions/index.html | 4 +- .../slack_bolt/adapter/pyramid/handler.html | 2 +- .../slack_bolt/adapter/pyramid/index.html | 2 +- .../adapter/sanic/async_handler.html | 14 +-- .../slack_bolt/adapter/sanic/index.html | 12 +-- .../adapter/socket_mode/aiohttp/index.html | 22 ++--- .../socket_mode/async_base_handler.html | 2 +- .../adapter/socket_mode/async_handler.html | 10 +- .../adapter/socket_mode/base_handler.html | 2 +- .../adapter/socket_mode/builtin/index.html | 10 +- .../slack_bolt/adapter/socket_mode/index.html | 10 +- .../socket_mode/websocket_client/index.html | 10 +- .../adapter/socket_mode/websockets/index.html | 22 ++--- .../adapter/starlette/async_handler.html | 2 +- .../slack_bolt/adapter/starlette/handler.html | 2 +- .../slack_bolt/adapter/starlette/index.html | 2 +- .../adapter/tornado/async_handler.html | 8 +- .../slack_bolt/adapter/tornado/handler.html | 8 +- .../slack_bolt/adapter/tornado/index.html | 8 +- .../slack_bolt/adapter/wsgi/handler.html | 14 ++- .../slack_bolt/adapter/wsgi/http_request.html | 4 +- .../slack_bolt/adapter/wsgi/index.html | 14 ++- docs/static/api-docs/slack_bolt/app/app.html | 52 +++++----- .../api-docs/slack_bolt/app/async_app.html | 60 ++++++------ .../api-docs/slack_bolt/app/async_server.html | 8 +- .../static/api-docs/slack_bolt/app/index.html | 46 +++++---- .../static/api-docs/slack_bolt/async_app.html | 96 +++++++++++-------- .../authorization/async_authorize.html | 22 ++--- .../authorization/async_authorize_args.html | 2 +- .../slack_bolt/authorization/authorize.html | 22 ++--- .../authorization/authorize_args.html | 2 +- .../authorization/authorize_result.html | 40 ++++---- .../slack_bolt/authorization/index.html | 40 ++++---- .../slack_bolt/context/async_context.html | 24 +++-- .../api-docs/slack_bolt/context/context.html | 24 +++-- .../api-docs/slack_bolt/context/index.html | 24 +++-- .../context/respond/async_respond.html | 2 +- .../slack_bolt/context/say/async_say.html | 6 +- .../slack_bolt/context/say/index.html | 6 +- .../api-docs/slack_bolt/context/say/say.html | 6 +- .../api-docs/slack_bolt/error/index.html | 10 +- docs/static/api-docs/slack_bolt/index.html | 84 +++++++++------- .../kwargs_injection/async_utils.html | 2 +- .../slack_bolt/kwargs_injection/index.html | 2 +- .../slack_bolt/kwargs_injection/utils.html | 2 +- .../lazy_listener/async_runner.html | 2 +- .../slack_bolt/listener/async_listener.html | 14 +-- .../async_listener_error_handler.html | 6 +- .../slack_bolt/listener/asyncio_runner.html | 2 +- .../slack_bolt/listener/custom_listener.html | 10 +- .../api-docs/slack_bolt/listener/index.html | 14 +-- .../slack_bolt/listener/listener.html | 4 +- .../listener/listener_error_handler.html | 6 +- .../slack_bolt/listener/thread_runner.html | 2 +- .../listener_matcher/async_builtins.html | 2 +- .../async_listener_matcher.html | 4 +- .../slack_bolt/listener_matcher/builtins.html | 2 +- .../custom_listener_matcher.html | 4 +- .../slack_bolt/listener_matcher/index.html | 4 +- .../api-docs/slack_bolt/logger/messages.html | 7 ++ .../slack_bolt/middleware/async_builtins.html | 8 +- .../middleware/async_custom_middleware.html | 8 +- .../async_middleware_error_handler.html | 6 +- .../async_attaching_function_token.html | 4 +- .../attaching_function_token.html | 4 +- .../attaching_function_token/index.html | 4 +- .../async_multi_teams_authorization.html | 4 +- .../async_single_team_authorization.html | 6 +- .../middleware/authorization/index.html | 10 +- .../multi_teams_authorization.html | 4 +- .../single_team_authorization.html | 6 +- .../middleware/custom_middleware.html | 6 +- .../async_ignoring_self_events.html | 2 +- .../ignoring_self_events.html | 2 +- .../ignoring_self_events/index.html | 2 +- .../api-docs/slack_bolt/middleware/index.html | 28 +++--- .../async_message_listener_matches.html | 2 +- .../message_listener_matches/index.html | 4 +- .../message_listener_matches.html | 4 +- .../middleware/middleware_error_handler.html | 8 +- .../request_verification/index.html | 2 +- .../request_verification.html | 2 +- .../middleware/ssl_check/index.html | 2 +- .../middleware/ssl_check/ssl_check.html | 2 +- .../middleware/url_verification/index.html | 2 +- .../url_verification/url_verification.html | 2 +- .../oauth/async_callback_options.html | 14 ++- .../slack_bolt/oauth/async_oauth_flow.html | 67 ++++++------- .../oauth/async_oauth_settings.html | 17 +--- .../slack_bolt/oauth/callback_options.html | 8 +- .../api-docs/slack_bolt/oauth/index.html | 65 +++++++------ .../api-docs/slack_bolt/oauth/internals.html | 10 +- .../api-docs/slack_bolt/oauth/oauth_flow.html | 65 +++++++------ .../slack_bolt/oauth/oauth_settings.html | 17 +--- .../api-docs/slack_bolt/util/utils.html | 7 ++ .../slack_bolt/workflows/step/async_step.html | 4 +- .../workflows/step/async_step_middleware.html | 2 +- .../slack_bolt/workflows/step/index.html | 2 +- .../slack_bolt/workflows/step/step.html | 4 +- .../workflows/step/step_middleware.html | 2 +- slack_bolt/version.py | 2 +- 125 files changed, 721 insertions(+), 662 deletions(-) diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html index 6fa6de3a4..daecab747 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html @@ -96,14 +96,12 @@

Args

) async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: AsyncOAuthFlow = self.app.oauth_flow - return await oauth_flow.handle_installation( + return await self.app.oauth_flow.handle_installation( # type: ignore[union-attr] AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: AsyncOAuthFlow = self.app.oauth_flow - return await oauth_flow.handle_callback( + return await self.app.oauth_flow.handle_callback( # type: ignore[union-attr] AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html index f100b1161..b48da50e8 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html @@ -96,14 +96,12 @@

Args

) async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: AsyncOAuthFlow = self.app.oauth_flow - return await oauth_flow.handle_installation( + return await self.app.oauth_flow.handle_installation( # type: ignore[union-attr] AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: AsyncOAuthFlow = self.app.oauth_flow - return await oauth_flow.handle_callback( + return await self.app.oauth_flow.handle_callback( # type: ignore[union-attr] AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html b/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html index a4941abca..72017bb48 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html @@ -45,7 +45,7 @@

Classes

Expand source code
class BaseSlackRequestHandler:
-    app: App  # type: ignore
+    app: Union[App, "AsyncApp"]  # type: ignore[name-defined]
     path: str
 
     async def dispatch(self, request: AsgiHttpRequest) -> BoltResponse:
@@ -68,13 +68,13 @@ 

Classes

return AsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) - if path == self.app.oauth_flow.redirect_uri_path: - bolt_response: BoltResponse = await self.handle_callback(request) + elif path == self.app.oauth_flow.redirect_uri_path: + bolt_response = await self.handle_callback(request) return AsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) if method == "POST" and path == self.path: - bolt_response: BoltResponse = await self.dispatch(request) + bolt_response = await self.dispatch(request) return AsgiHttpResponse(status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body) return AsgiHttpResponse(status=404, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Not Found") @@ -91,7 +91,7 @@

Classes

async def __call__(self, scope: scope_type, receive: Callable, send: Callable) -> None: if scope["type"] == "http": response: AsgiHttpResponse = await self._get_http_response( - scope["method"], scope["path"], AsgiHttpRequest(scope, receive) + method=scope["method"], path=scope["path"], request=AsgiHttpRequest(scope, receive) # type: ignore[arg-type] ) await send(response.get_response_start()) await send(response.get_response_body()) @@ -99,7 +99,7 @@

Classes

if scope["type"] == "lifespan": await send(await self._handle_lifespan(receive)) return - raise TypeError(f"Unsupported scope type: {scope['type']}")
+ raise TypeError(f"Unsupported scope type: {scope['type']!r}")

Subclasses

Class variables

-
var appApp
+
var app : Union[App, AsyncApp]
diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html index 66bead34a..51258e557 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html @@ -65,7 +65,7 @@

Args

Expand source code
class SlackRequestHandler(BaseSlackRequestHandler):
-    def __init__(self, app: App, path: str = "/slack/events"):  # type: ignore
+    def __init__(self, app: App, path: str = "/slack/events"):
         """Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers.
         This can be used for production deployment.
 
@@ -94,14 +94,12 @@ 

Args

) async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_installation( + return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_callback( + return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) )
diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html b/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html index cdf7c4d3f..cc9a50c8e 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html @@ -50,10 +50,10 @@

Classes

def __init__(self, scope: scope_type, receive: Callable): self.receive = receive - self.query_string = str(scope["query_string"], ENCODING) - self.raw_headers = scope["headers"] + self.query_string = str(scope["query_string"], ENCODING) # type: ignore[arg-type] + self.raw_headers: Iterable[Tuple[bytes, bytes]] = scope["headers"] # type: ignore[assignment] - def get_headers(self) -> Dict[str, str]: + def get_headers(self) -> Dict[str, Union[str, Sequence[str]]]: return {str(header[0], ENCODING): str(header[1], (ENCODING)) for header in self.raw_headers} async def get_raw_body(self) -> str: @@ -64,7 +64,7 @@

Classes

if chunk["type"] != "http.request": raise Exception("Body chunks could not be received from asgi server") - chunks.extend(chunk.get("body", b"")) + chunks.extend(chunk.get("body", b"")) # type: ignore[arg-type] if not chunk.get("more_body", False): break return bytes(chunks).decode(ENCODING) @@ -87,7 +87,7 @@

Instance variables

Methods

-def get_headers(self) ‑> Dict[str, str] +def get_headers(self) ‑> Dict[str, Union[str, Sequence[str]]]
diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/index.html index 0f6acfa46..9e43e503e 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/index.html @@ -96,7 +96,7 @@

Args

Expand source code
class SlackRequestHandler(BaseSlackRequestHandler):
-    def __init__(self, app: App, path: str = "/slack/events"):  # type: ignore
+    def __init__(self, app: App, path: str = "/slack/events"):
         """Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers.
         This can be used for production deployment.
 
@@ -125,14 +125,12 @@ 

Args

) async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_installation( + return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_callback( + return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) )
diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html index 4627cb0e8..21ad6058d 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html @@ -67,7 +67,7 @@

Classes

Expand source code
class ChaliceSlackRequestHandler:
-    def __init__(self, app: App, chalice: Chalice, lambda_client: Optional[BaseClient] = None):  # type: ignore
+    def __init__(self, app: App, chalice: Chalice, lambda_client: Optional[BaseClient] = None):
         self.app = app
         self.chalice = chalice
         self.logger = get_bolt_app_logger(app.name, ChaliceSlackRequestHandler, app.logger)
@@ -78,7 +78,7 @@ 

Classes

LocalLambdaClient, ) - lambda_client = LocalLambdaClient(self.chalice, None) + lambda_client = LocalLambdaClient(self.chalice, None) # type: ignore[arg-type] except ImportError: logging.info("Failed to load LocalLambdaClient for CLI mode.") pass @@ -99,7 +99,7 @@

Classes

root.removeHandler(handler) def handle(self, request: Request): - body: str = request.raw_body.decode("utf-8") if request.raw_body else "" + body: str = request.raw_body.decode("utf-8") if request.raw_body else "" # type: ignore[union-attr] self.logger.debug(f"Incoming request: {request.to_dict()}, body: {body}") method = request.method @@ -121,7 +121,7 @@

Classes

bolt_resp = oauth_flow.handle_installation(bolt_req) return to_chalice_response(bolt_resp) elif method == "POST": - bolt_req: BoltRequest = to_bolt_request(request, body) + bolt_req = to_bolt_request(request, body) # https://docs.aws.amazon.com/lambda/latest/dg/python-context.html aws_lambda_function_name = self.chalice.lambda_context.function_name bolt_req.context["aws_lambda_function_name"] = aws_lambda_function_name @@ -130,7 +130,7 @@

Classes

aws_response = to_chalice_response(bolt_resp) return aws_response elif method == "NONE": - bolt_req: BoltRequest = to_bolt_request(request, body) + bolt_req = to_bolt_request(request, body) bolt_resp = self.app.dispatch(bolt_req) aws_response = to_chalice_response(bolt_resp) return aws_response diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html index ae14fefda..eee38727f 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html @@ -56,7 +56,7 @@

Classes

chalice_request: dict = request.context["chalice_request"] request.headers["x-slack-bolt-lazy-only"] = ["1"] - request.headers["x-slack-bolt-lazy-function-name"] = [request.lazy_function_name] + request.headers["x-slack-bolt-lazy-function-name"] = [request.lazy_function_name] # type: ignore[list-item] payload = { "method": "NONE", "headers": {k: v[0] for k, v in request.headers.items()}, diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html index 34587e3ad..f836f583c 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html @@ -67,7 +67,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
         self.logger = get_bolt_app_logger(app.name, SlackRequestHandler, app.logger)
         self.app.listener_runner.lazy_listener_runner = LambdaLazyListenerRunner(self.logger)
diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html
index f70556b72..9b1e30b2a 100644
--- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html
+++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html
@@ -77,7 +77,7 @@ 

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
         self.logger = get_bolt_app_logger(app.name, SlackRequestHandler, app.logger)
         self.app.listener_runner.lazy_listener_runner = LambdaLazyListenerRunner(self.logger)
diff --git a/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html b/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html
index dc10c76d9..a8615c7f2 100644
--- a/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html
@@ -61,7 +61,7 @@ 

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self, req: Request, resp: Response) -> str:
@@ -77,7 +77,7 @@ 

Classes

set_response(bolt_resp, resp) return bolt_resp.body or "" elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" diff --git a/docs/static/api-docs/slack_bolt/adapter/bottle/index.html b/docs/static/api-docs/slack_bolt/adapter/bottle/index.html index b158ebaf7..941353ccd 100644 --- a/docs/static/api-docs/slack_bolt/adapter/bottle/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/bottle/index.html @@ -53,7 +53,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self, req: Request, resp: Response) -> str:
@@ -69,7 +69,7 @@ 

Classes

set_response(bolt_resp, resp) return bolt_resp.body or "" elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" diff --git a/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html b/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html index 263c294c7..9aa328d64 100644 --- a/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html @@ -67,7 +67,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self) -> bytes:
@@ -85,7 +85,7 @@ 

Classes

set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(build_bolt_request()) + bolt_resp = self.app.dispatch(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") diff --git a/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html b/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html index 8430cbe29..142190a72 100644 --- a/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html @@ -53,7 +53,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self) -> bytes:
@@ -71,7 +71,7 @@ 

Classes

set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(build_bolt_request()) + bolt_resp = self.app.dispatch(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") diff --git a/docs/static/api-docs/slack_bolt/adapter/django/handler.html b/docs/static/api-docs/slack_bolt/adapter/django/handler.html index efd5fc50d..6509e1737 100644 --- a/docs/static/api-docs/slack_bolt/adapter/django/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/django/handler.html @@ -175,7 +175,7 @@

Inherited members

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
         listener_runner = self.app.listener_runner
         # This runner closes all thread-local connections in the thread when an execution completes
@@ -238,7 +238,7 @@ 

Inherited members

bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_django_response(bolt_resp) elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_django_response(bolt_resp) return HttpResponse(status=404, content=b"Not Found")
diff --git a/docs/static/api-docs/slack_bolt/adapter/django/index.html b/docs/static/api-docs/slack_bolt/adapter/django/index.html index 2fdc5a473..6fcb62b8d 100644 --- a/docs/static/api-docs/slack_bolt/adapter/django/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/django/index.html @@ -53,7 +53,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
         listener_runner = self.app.listener_runner
         # This runner closes all thread-local connections in the thread when an execution completes
@@ -116,7 +116,7 @@ 

Classes

bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_django_response(bolt_resp) elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_django_response(bolt_resp) return HttpResponse(status=404, content=b"Not Found")
diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html b/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html index 05c09fa3a..050729721 100644 --- a/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html +++ b/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html @@ -62,7 +62,7 @@

Classes

app.add_route("/slack/events", AsyncSlackAppResource(app)) """ - def __init__(self, app: AsyncApp): # type: ignore + def __init__(self, app: AsyncApp): if falcon_version.__version__.startswith("2."): raise BoltError("This ASGI compatible adapter requires Falcon version >= 3.0") diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/index.html b/docs/static/api-docs/slack_bolt/adapter/falcon/index.html index 39ae0b849..b54b3cd22 100644 --- a/docs/static/api-docs/slack_bolt/adapter/falcon/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/falcon/index.html @@ -70,7 +70,7 @@

Classes

api.add_route("/slack/events", SlackAppResource(app)) """ - def __init__(self, app: App): # type: ignore + def __init__(self, app: App): self.app = app def on_get(self, req: Request, resp: Response): diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html b/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html index 6a4bbe0d1..a00b3be11 100644 --- a/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html +++ b/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html @@ -59,7 +59,7 @@

Classes

api.add_route("/slack/events", SlackAppResource(app)) """ - def __init__(self, app: App): # type: ignore + def __init__(self, app: App): self.app = app def on_get(self, req: Request, resp: Response): diff --git a/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html index 1432a30d5..409efd900 100644 --- a/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html @@ -46,7 +46,7 @@

Classes

Expand source code
class AsyncSlackRequestHandler:
-    def __init__(self, app: AsyncApp):  # type: ignore
+    def __init__(self, app: AsyncApp):
         self.app = app
 
     async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response:
diff --git a/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html b/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html
index 7a316a9fc..996afbf54 100644
--- a/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html
+++ b/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html
@@ -53,7 +53,7 @@ 

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response:
diff --git a/docs/static/api-docs/slack_bolt/adapter/flask/handler.html b/docs/static/api-docs/slack_bolt/adapter/flask/handler.html
index ddedb27e7..599ed63be 100644
--- a/docs/static/api-docs/slack_bolt/adapter/flask/handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/flask/handler.html
@@ -61,7 +61,7 @@ 

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self, req: Request) -> Response:
@@ -75,7 +75,7 @@ 

Classes

bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_flask_response(bolt_resp) elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404)
diff --git a/docs/static/api-docs/slack_bolt/adapter/flask/index.html b/docs/static/api-docs/slack_bolt/adapter/flask/index.html index e4a9559af..15bfa55f2 100644 --- a/docs/static/api-docs/slack_bolt/adapter/flask/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/flask/index.html @@ -53,7 +53,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self, req: Request) -> Response:
@@ -67,7 +67,7 @@ 

Classes

bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_flask_response(bolt_resp) elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404)
diff --git a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html index be307b43f..5ff58c6c5 100644 --- a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html @@ -77,7 +77,7 @@

Inherited members

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
         # Note that lazy listener is not supported
         self.app.listener_runner.lazy_listener_runner = NoopLazyListenerRunner()
@@ -94,7 +94,7 @@ 

Inherited members

bolt_resp = self.app.oauth_flow.handle_installation(bolt_req) return to_flask_response(bolt_resp) elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404)
diff --git a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html index abd4ece2c..7d5e9ee63 100644 --- a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html @@ -53,7 +53,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
         # Note that lazy listener is not supported
         self.app.listener_runner.lazy_listener_runner = NoopLazyListenerRunner()
@@ -70,7 +70,7 @@ 

Classes

bolt_resp = self.app.oauth_flow.handle_installation(bolt_req) return to_flask_response(bolt_resp) elif req.method == "POST": - bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) + bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404)
diff --git a/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html b/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html index 6b16a1f40..fa64e6931 100644 --- a/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html @@ -61,7 +61,7 @@

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self, request: Request) -> Response:
diff --git a/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html b/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html
index f9c95d2bd..d962e458b 100644
--- a/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html
+++ b/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html
@@ -53,7 +53,7 @@ 

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     def handle(self, request: Request) -> Response:
diff --git a/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html
index ab36f1dcc..a94007bcc 100644
--- a/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html
@@ -34,7 +34,7 @@ 

Module slack_bolt.adapter.sanic.async_handler

Functions
-def to_async_bolt_request(req: sanic.request.types.Request) ‑> AsyncBoltRequest +def to_async_bolt_request(req: sanic.request.types.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> AsyncBoltRequest
@@ -61,22 +61,22 @@

Classes

Expand source code
class AsyncSlackRequestHandler:
-    def __init__(self, app: AsyncApp):  # type: ignore
+    def __init__(self, app: AsyncApp):
         self.app = app
 
-    async def handle(self, req: Request) -> HTTPResponse:
+    async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> HTTPResponse:
         if req.method == "GET":
             if self.app.oauth_flow is not None:
                 oauth_flow: AsyncOAuthFlow = self.app.oauth_flow
                 if req.path == oauth_flow.install_path:
-                    bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req))
+                    bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req, addition_context_properties))
                     return to_sanic_response(bolt_resp)
                 elif req.path == oauth_flow.redirect_uri_path:
-                    bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req))
+                    bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req, addition_context_properties))
                     return to_sanic_response(bolt_resp)
 
         elif req.method == "POST":
-            bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req))
+            bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req, addition_context_properties))
             return to_sanic_response(bolt_resp)
 
         return HTTPResponse(
@@ -87,7 +87,7 @@ 

Classes

Methods

-async def handle(self, req: sanic.request.types.Request) ‑> sanic.response.types.HTTPResponse +async def handle(self, req: sanic.request.types.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> sanic.response.types.HTTPResponse
diff --git a/docs/static/api-docs/slack_bolt/adapter/sanic/index.html b/docs/static/api-docs/slack_bolt/adapter/sanic/index.html index 60073cc63..be50d1ab4 100644 --- a/docs/static/api-docs/slack_bolt/adapter/sanic/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/sanic/index.html @@ -53,22 +53,22 @@

Classes

Expand source code
class AsyncSlackRequestHandler:
-    def __init__(self, app: AsyncApp):  # type: ignore
+    def __init__(self, app: AsyncApp):
         self.app = app
 
-    async def handle(self, req: Request) -> HTTPResponse:
+    async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> HTTPResponse:
         if req.method == "GET":
             if self.app.oauth_flow is not None:
                 oauth_flow: AsyncOAuthFlow = self.app.oauth_flow
                 if req.path == oauth_flow.install_path:
-                    bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req))
+                    bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req, addition_context_properties))
                     return to_sanic_response(bolt_resp)
                 elif req.path == oauth_flow.redirect_uri_path:
-                    bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req))
+                    bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req, addition_context_properties))
                     return to_sanic_response(bolt_resp)
 
         elif req.method == "POST":
-            bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req))
+            bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req, addition_context_properties))
             return to_sanic_response(bolt_resp)
 
         return HTTPResponse(
@@ -79,7 +79,7 @@ 

Classes

Methods

-async def handle(self, req: sanic.request.types.Request) ‑> sanic.response.types.HTTPResponse +async def handle(self, req: sanic.request.types.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> sanic.response.types.HTTPResponse
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html index b3face018..9f2212fd3 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html @@ -47,13 +47,13 @@

Classes

Expand source code
class AsyncSocketModeHandler(AsyncBaseSocketModeHandler):
-    app: AsyncApp  # type: ignore
+    app: AsyncApp
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: AsyncApp,  # type: ignore
+        app: AsyncApp,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[AsyncWebClient] = None,
@@ -69,9 +69,9 @@ 

Classes

proxy=proxy, ping_interval=ping_interval, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = await run_async_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start)
@@ -134,13 +134,13 @@

Args

Expand source code
class SocketModeHandler(AsyncBaseSocketModeHandler):
-    app: App  # type: ignore
+    app: App
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: App,  # type: ignore
+        app: App,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[AsyncWebClient] = None,
@@ -162,13 +162,13 @@ 

Args

self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, - web_client=web_client if web_client is not None else app.client, + web_client=web_client if web_client is not None else app.client, # type: ignore[arg-type] proxy=proxy, ping_interval=ping_interval, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start)
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html index 8a40967e5..b9a211803 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html @@ -46,7 +46,7 @@

Classes

Expand source code
class AsyncBaseSocketModeHandler:
-    app: Union[App, AsyncApp]  # type: ignore
+    app: Union[App, AsyncApp]
     client: AsyncBaseSocketModeClient
 
     async def handle(self, client: AsyncBaseSocketModeClient, req: SocketModeRequest) -> None:
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html
index d9a513b85..7234d62f9 100644
--- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html
@@ -47,13 +47,13 @@ 

Classes

Expand source code
class AsyncSocketModeHandler(AsyncBaseSocketModeHandler):
-    app: AsyncApp  # type: ignore
+    app: AsyncApp
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: AsyncApp,  # type: ignore
+        app: AsyncApp,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[AsyncWebClient] = None,
@@ -69,9 +69,9 @@ 

Classes

proxy=proxy, ping_interval=ping_interval, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = await run_async_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start)
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html index c6555d0dc..f8d221d9f 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html @@ -48,7 +48,7 @@

Classes

Expand source code
class BaseSocketModeHandler:
-    app: App  # type: ignore
+    app: App
     client: BaseSocketModeClient
 
     def handle(self, client: BaseSocketModeClient, req: SocketModeRequest) -> None:
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html
index 840f552f6..776dac0c1 100644
--- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html
+++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html
@@ -76,13 +76,13 @@ 

Args

Expand source code
class SocketModeHandler(BaseSocketModeHandler):
-    app: App  # type: ignore
+    app: App
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: App,  # type: ignore
+        app: App,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[WebClient] = None,
@@ -129,9 +129,9 @@ 

Args

receive_buffer_size=receive_buffer_size, concurrency=concurrency, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) send_response(client, req, bolt_resp, start)
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html index 14affb94c..b45fa55fb 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html @@ -123,13 +123,13 @@

Args

Expand source code
class SocketModeHandler(BaseSocketModeHandler):
-    app: App  # type: ignore
+    app: App
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: App,  # type: ignore
+        app: App,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[WebClient] = None,
@@ -176,9 +176,9 @@ 

Args

receive_buffer_size=receive_buffer_size, concurrency=concurrency, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) send_response(client, req, bolt_resp, start)
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html index 7d694db57..074342c85 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html @@ -72,13 +72,13 @@

Args

Expand source code
class SocketModeHandler(BaseSocketModeHandler):
-    app: App  # type: ignore
+    app: App
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: App,  # type: ignore
+        app: App,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[WebClient] = None,
@@ -119,9 +119,9 @@ 

Args

proxy_type=proxy_type, trace_enabled=trace_enabled, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) send_response(client, req, bolt_resp, start)
diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html index fa84e50d9..415070179 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html @@ -49,13 +49,13 @@

Classes

Expand source code
class AsyncSocketModeHandler(AsyncBaseSocketModeHandler):
-    app: AsyncApp  # type: ignore
+    app: AsyncApp
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: AsyncApp,  # type: ignore
+        app: AsyncApp,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[AsyncWebClient] = None,
@@ -69,9 +69,9 @@ 

Classes

web_client=web_client if web_client is not None else app.client, ping_interval=ping_interval, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = await run_async_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start)
@@ -135,13 +135,13 @@

Args

Expand source code
class SocketModeHandler(AsyncBaseSocketModeHandler):
-    app: App  # type: ignore
+    app: App
     app_token: str
     client: SocketModeClient
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
-        app: App,  # type: ignore
+        app: App,
         app_token: Optional[str] = None,
         logger: Optional[Logger] = None,
         web_client: Optional[AsyncWebClient] = None,
@@ -165,12 +165,12 @@ 

Args

self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, - web_client=web_client if web_client is not None else app.client, + web_client=web_client if web_client is not None else app.client, # type: ignore[arg-type] ping_interval=ping_interval, ) - self.client.socket_mode_request_listeners.append(self.handle) + self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] - async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: + async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start)
diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html index c303260d4..d5a8ce076 100644 --- a/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html @@ -61,7 +61,7 @@

Classes

Expand source code
class AsyncSlackRequestHandler:
-    def __init__(self, app: AsyncApp):  # type: ignore
+    def __init__(self, app: AsyncApp):
         self.app = app
 
     async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response:
diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html b/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html
index f70139d54..b5297f46e 100644
--- a/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html
@@ -61,7 +61,7 @@ 

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response:
diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/index.html b/docs/static/api-docs/slack_bolt/adapter/starlette/index.html
index 43a853641..02b84c06c 100644
--- a/docs/static/api-docs/slack_bolt/adapter/starlette/index.html
+++ b/docs/static/api-docs/slack_bolt/adapter/starlette/index.html
@@ -57,7 +57,7 @@ 

Classes

Expand source code
class SlackRequestHandler:
-    def __init__(self, app: App):  # type: ignore
+    def __init__(self, app: App):
         self.app = app
 
     async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response:
diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html
index 19c5486d9..6926e3eb5 100644
--- a/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html
@@ -60,7 +60,7 @@ 

Classes

Expand source code
class AsyncSlackEventsHandler(RequestHandler):
-    def initialize(self, app: AsyncApp):  # type: ignore
+    def initialize(self, app: AsyncApp):
         self.app = app
 
     async def post(self):
@@ -104,12 +104,12 @@ 

Methods

Expand source code
class AsyncSlackOAuthHandler(RequestHandler):
-    def initialize(self, app: AsyncApp):  # type: ignore
+    def initialize(self, app: AsyncApp):
         self.app = app
 
     async def get(self):
-        if self.app.oauth_flow is not None:  # type: ignore
-            oauth_flow: AsyncOAuthFlow = self.app.oauth_flow  # type: ignore
+        if self.app.oauth_flow is not None:
+            oauth_flow: AsyncOAuthFlow = self.app.oauth_flow
             if self.request.path == oauth_flow.install_path:
                 bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(self.request))
                 set_response(self, bolt_resp)
diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html b/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html
index 241a71824..29d364937 100644
--- a/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html
@@ -66,7 +66,7 @@ 

Classes

Expand source code
class SlackEventsHandler(RequestHandler):
-    def initialize(self, app: App):  # type: ignore
+    def initialize(self, app: App):
         self.app = app
 
     def post(self):
@@ -110,12 +110,12 @@ 

Methods

Expand source code
class SlackOAuthHandler(RequestHandler):
-    def initialize(self, app: App):  # type: ignore
+    def initialize(self, app: App):
         self.app = app
 
     def get(self):
-        if self.app.oauth_flow is not None:  # type: ignore
-            oauth_flow: OAuthFlow = self.app.oauth_flow  # type: ignore
+        if self.app.oauth_flow is not None:
+            oauth_flow: OAuthFlow = self.app.oauth_flow
             if self.request.path == oauth_flow.install_path:
                 bolt_resp = oauth_flow.handle_installation(to_bolt_request(self.request))
                 set_response(self, bolt_resp)
diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/index.html b/docs/static/api-docs/slack_bolt/adapter/tornado/index.html
index 4ab47e8f5..920fb845f 100644
--- a/docs/static/api-docs/slack_bolt/adapter/tornado/index.html
+++ b/docs/static/api-docs/slack_bolt/adapter/tornado/index.html
@@ -62,7 +62,7 @@ 

Classes

Expand source code
class SlackEventsHandler(RequestHandler):
-    def initialize(self, app: App):  # type: ignore
+    def initialize(self, app: App):
         self.app = app
 
     def post(self):
@@ -106,12 +106,12 @@ 

Methods

Expand source code
class SlackOAuthHandler(RequestHandler):
-    def initialize(self, app: App):  # type: ignore
+    def initialize(self, app: App):
         self.app = app
 
     def get(self):
-        if self.app.oauth_flow is not None:  # type: ignore
-            oauth_flow: OAuthFlow = self.app.oauth_flow  # type: ignore
+        if self.app.oauth_flow is not None:
+            oauth_flow: OAuthFlow = self.app.oauth_flow
             if self.request.path == oauth_flow.install_path:
                 bolt_resp = oauth_flow.handle_installation(to_bolt_request(self.request))
                 set_response(self, bolt_resp)
diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html
index 6d94e28e4..ad7ba0ce8 100644
--- a/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html
+++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html
@@ -100,14 +100,12 @@ 

Args

) def handle_installation(self, request: WsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_installation( + return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) def handle_callback(self, request: WsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_callback( + return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) @@ -115,17 +113,17 @@

Args

if request.method == "GET": if self.app.oauth_flow is not None: if request.path == self.app.oauth_flow.install_path: - bolt_response: BoltResponse = self.handle_installation(request) + bolt_response = self.handle_installation(request) return WsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) - if request.path == self.app.oauth_flow.redirect_uri_path: - bolt_response: BoltResponse = self.handle_callback(request) + elif request.path == self.app.oauth_flow.redirect_uri_path: + bolt_response = self.handle_callback(request) return WsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) if request.method == "POST" and request.path == self.path: - bolt_response: BoltResponse = self.dispatch(request) + bolt_response = self.dispatch(request) return WsgiHttpResponse(status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body) return WsgiHttpResponse(status=404, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Not Found") diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html index 02b4d22d8..b8d462a1d 100644 --- a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html +++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html @@ -63,7 +63,7 @@

Classes

self.protocol: str = environ.get("SERVER_PROTOCOL", "") self.environ = environ - def get_headers(self) -> Dict[str, str]: + def get_headers(self) -> Dict[str, Union[str, Sequence[str]]]: headers = {} for key, value in self.environ.items(): if key in {"CONTENT_LENGTH", "CONTENT_TYPE"}: @@ -112,7 +112,7 @@

Methods

-def get_headers(self) ‑> Dict[str, str] +def get_headers(self) ‑> Dict[str, Union[str, Sequence[str]]]
diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html index 18860e80c..2ff190ed0 100644 --- a/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html @@ -119,14 +119,12 @@

Args

) def handle_installation(self, request: WsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_installation( + return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) def handle_callback(self, request: WsgiHttpRequest) -> BoltResponse: - oauth_flow: OAuthFlow = self.app.oauth_flow - return oauth_flow.handle_callback( + return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) @@ -134,17 +132,17 @@

Args

if request.method == "GET": if self.app.oauth_flow is not None: if request.path == self.app.oauth_flow.install_path: - bolt_response: BoltResponse = self.handle_installation(request) + bolt_response = self.handle_installation(request) return WsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) - if request.path == self.app.oauth_flow.redirect_uri_path: - bolt_response: BoltResponse = self.handle_callback(request) + elif request.path == self.app.oauth_flow.redirect_uri_path: + bolt_response = self.handle_callback(request) return WsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) if request.method == "POST" and request.path == self.path: - bolt_response: BoltResponse = self.dispatch(request) + bolt_response = self.dispatch(request) return WsgiHttpResponse(status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body) return WsgiHttpResponse(status=404, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Not Found") diff --git a/docs/static/api-docs/slack_bolt/app/app.html b/docs/static/api-docs/slack_bolt/app/app.html index 8a88e40af..f3a9808c9 100644 --- a/docs/static/api-docs/slack_bolt/app/app.html +++ b/docs/static/api-docs/slack_bolt/app/app.html @@ -238,7 +238,8 @@

Args

listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. """ - signing_secret = signing_secret or os.environ.get("SLACK_SIGNING_SECRET", "") + if signing_secret is None: + signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") token = token or os.environ.get("SLACK_BOT_TOKEN") self._name: str = name or inspect.stack()[1].filename.split(os.path.sep)[-1] @@ -275,7 +276,7 @@

Args

self._before_authorize: Optional[Middleware] = None if before_authorize is not None: - if isinstance(before_authorize, Callable): + if callable(before_authorize): self._before_authorize = CustomMiddleware( app_name=self._name, func=before_authorize, @@ -305,7 +306,7 @@

Args

client_id=settings.client_id if settings is not None else None, client_secret=settings.client_secret if settings is not None else None, logger=self._framework_logger, - bot_only=installation_store_bot_only, + bot_only=installation_store_bot_only or False, client=self._client, # for proxy use cases etc. user_token_resolution=(settings.user_token_resolution if settings is not None else "authed_user"), ) @@ -333,7 +334,8 @@

Args

logger=self._framework_logger, ) self._installation_store = installation_store - self._oauth_flow.settings.installation_store = installation_store + if installation_store is not None: + self._oauth_flow.settings.installation_store = installation_store if self._oauth_flow._client is None: self._oauth_flow._client = self._client @@ -347,11 +349,12 @@

Args

logger=self._framework_logger, ) self._installation_store = installation_store - oauth_settings.installation_store = installation_store + if installation_store is not None: + oauth_settings.installation_store = installation_store self._oauth_flow = OAuthFlow(client=self.client, logger=self.logger, settings=oauth_settings) if self._authorize is None: self._authorize = self._oauth_flow.settings.authorize - self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes + self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes # type: ignore[attr-defined] # noqa: E501 if (self._installation_store is not None or self._authorize is not None) and self._token is not None: self._token = None @@ -364,7 +367,7 @@

Args

if app_bot_only != oauth_flow_bot_only: self.logger.warning(warning_bot_only_conflicts()) self._oauth_flow.settings.installation_store_bot_only = app_bot_only - self._authorize.bot_only = app_bot_only + self._authorize.bot_only = app_bot_only # type: ignore[union-attr] self._tokens_revocation_listeners: Optional[TokenRevocationListeners] = None if self._installation_store is not None: @@ -393,7 +396,7 @@

Args

executor=listener_executor, ), ) - self._middleware_error_handler = DefaultMiddlewareErrorHandler( + self._middleware_error_handler: MiddlewareErrorHandler = DefaultMiddlewareErrorHandler( logger=self._framework_logger, ) @@ -460,7 +463,7 @@

Args

) else: raise BoltError(error_token_required()) - else: + elif self._authorize is not None: self._middleware_list.append( MultiTeamsAuthorization( authorize=self._authorize, @@ -469,6 +472,9 @@

Args

user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) + else: + raise BoltError(error_oauth_flow_or_authorize_required()) + if ignoring_self_events_enabled is True: self._middleware_list.append(IgnoringSelfEvents(base_logger=self._base_logger)) if url_verification_enabled is True: @@ -572,7 +578,7 @@

Args

middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(debug_applying_middleware(middleware.name)) - resp = middleware.process(req=req, resp=resp, next=middleware_next) + resp = middleware.process(req=req, resp=resp, next=middleware_next) # type: ignore[arg-type] if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack @@ -599,9 +605,11 @@

Args

for listener in self._listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) - if listener.matches(req=req, resp=resp): + if listener.matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) + middleware_resp, next_was_not_called = listener.run_middleware( + req=req, resp=resp # type: ignore[arg-type] + ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: @@ -623,7 +631,7 @@

Args

self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = self._listener_runner.run( request=req, - response=resp, + response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) @@ -693,7 +701,7 @@

Args

if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) - elif isinstance(middleware_or_callable, Callable): + elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( app_name=self.name, @@ -762,9 +770,9 @@

Args

if isinstance(callback_id, (str, Pattern)): step = WorkflowStep( callback_id=callback_id, - edit=edit, - save=save, - execute=execute, + edit=edit, # type: ignore[arg-type] + save=save, # type: ignore[arg-type] + execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, WorkflowStepBuilder): @@ -1414,7 +1422,7 @@

Args

# the registration should return the original function. value_to_return = functions[0] - listener_matchers = [ + listener_matchers: List[ListenerMatcher] = [ CustomListenerMatcher(app_name=self.name, func=f, base_logger=self._base_logger) for f in (matchers or []) ] listener_matchers.insert(0, primary_matcher) @@ -1422,7 +1430,7 @@

Args

for m in middleware or []: if isinstance(m, Middleware): listener_middleware.append(m) - elif isinstance(m, Callable): + elif callable(m): listener_middleware.append(CustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) @@ -2096,7 +2104,7 @@

Args

body="", query=query, # email.message.Message's mapping interface is dict compatible - headers=self.headers, # type:ignore + headers=self.headers, ) bolt_resp = _bolt_oauth_flow.handle_installation(bolt_req) self._send_bolt_response(bolt_resp) @@ -2105,7 +2113,7 @@

Args

body="", query=query, # email.message.Message's mapping interface is dict compatible - headers=self.headers, # type:ignore + headers=self.headers, ) bolt_resp = _bolt_oauth_flow.handle_callback(bolt_req) self._send_bolt_response(bolt_resp) @@ -2126,7 +2134,7 @@

Args

body=request_body, query=query, # email.message.Message's mapping interface is dict compatible - headers=self.headers, # type:ignore + headers=self.headers, ) bolt_resp: BoltResponse = _bolt_app.dispatch(bolt_req) self._send_bolt_response(bolt_resp) diff --git a/docs/static/api-docs/slack_bolt/app/async_app.html b/docs/static/api-docs/slack_bolt/app/async_app.html index cc1cabe0e..b5c7251da 100644 --- a/docs/static/api-docs/slack_bolt/app/async_app.html +++ b/docs/static/api-docs/slack_bolt/app/async_app.html @@ -227,7 +227,8 @@

Args

oauth_flow: Instantiated `slack_bolt.oauth.AsyncOAuthFlow`. This is always prioritized over oauth_settings. verification_token: Deprecated verification mechanism. This can used only for ssl_check requests. """ - signing_secret = signing_secret or os.environ.get("SLACK_SIGNING_SECRET", "") + if signing_secret is None: + signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") token = token or os.environ.get("SLACK_BOT_TOKEN") self._name: str = name or inspect.stack()[1].filename.split(os.path.sep)[-1] @@ -263,7 +264,7 @@

Args

self._async_before_authorize: Optional[AsyncMiddleware] = None if before_authorize is not None: - if isinstance(before_authorize, Callable): + if callable(before_authorize): self._async_before_authorize = AsyncCustomMiddleware( app_name=self._name, func=before_authorize, @@ -293,7 +294,7 @@

Args

client_id=settings.client_id if settings is not None else None, client_secret=settings.client_secret if settings is not None else None, logger=self._framework_logger, - bot_only=installation_store_bot_only, + bot_only=installation_store_bot_only or False, client=self._async_client, # for proxy use cases etc. user_token_resolution=(settings.user_token_resolution if settings is not None else "authed_user"), ) @@ -324,7 +325,8 @@

Args

logger=self._framework_logger, ) self._async_installation_store = installation_store - self._async_oauth_flow.settings.installation_store = installation_store + if installation_store is not None: + self._async_oauth_flow.settings.installation_store = installation_store if self._async_oauth_flow._async_client is None: self._async_oauth_flow._async_client = self._async_client @@ -341,12 +343,13 @@

Args

logger=self._framework_logger, ) self._async_installation_store = installation_store - oauth_settings.installation_store = installation_store + if installation_store is not None: + oauth_settings.installation_store = installation_store self._async_oauth_flow = AsyncOAuthFlow(client=self._async_client, logger=self.logger, settings=oauth_settings) if self._async_authorize is None: self._async_authorize = self._async_oauth_flow.settings.authorize - self._async_authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes + self._async_authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes # type: ignore[attr-defined] # noqa: E501 if (self._async_installation_store is not None or self._async_authorize is not None) and self._token is not None: self._token = None @@ -359,7 +362,7 @@

Args

if app_bot_only != oauth_flow_bot_only: self.logger.warning(warning_bot_only_conflicts()) self._async_oauth_flow.settings.installation_store_bot_only = app_bot_only - self._async_authorize.bot_only = app_bot_only + self._async_authorize.bot_only = app_bot_only # type: ignore[union-attr] self._async_tokens_revocation_listeners: Optional[AsyncTokenRevocationListeners] = None if self._async_installation_store is not None: @@ -383,7 +386,7 @@

Args

logger=self._framework_logger, ), ) - self._async_middleware_error_handler = AsyncDefaultMiddlewareErrorHandler( + self._async_middleware_error_handler: AsyncMiddlewareErrorHandler = AsyncDefaultMiddlewareErrorHandler( logger=self._framework_logger, ) @@ -442,7 +445,7 @@

Args

) else: raise BoltError(error_token_required()) - else: + elif self._async_authorize is not None: self._async_middleware_list.append( AsyncMultiTeamsAuthorization( authorize=self._async_authorize, @@ -451,6 +454,8 @@

Args

user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) + else: + raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: self._async_middleware_list.append(AsyncIgnoringSelfEvents(base_logger=self._base_logger)) @@ -584,7 +589,9 @@

Args

middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(f"Applying {middleware.name}") - resp = await middleware.async_process(req=req, resp=resp, next=async_middleware_next) + resp = await middleware.async_process( + req=req, resp=resp, next=async_middleware_next # type: ignore[arg-type] + ) if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack @@ -611,12 +618,11 @@

Args

for listener in self._async_listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) - if await listener.async_matches(req=req, resp=resp): + if await listener.async_matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - ( - middleware_resp, - next_was_not_called, - ) = await listener.run_async_middleware(req=req, resp=resp) + (middleware_resp, next_was_not_called) = await listener.run_async_middleware( + req=req, resp=resp # type: ignore[arg-type] + ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: @@ -638,7 +644,7 @@

Args

self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = await self._async_listener_runner.run( request=req, - response=resp, + response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) @@ -705,7 +711,7 @@

Args

if isinstance(middleware_or_callable, AsyncMiddleware): middleware: AsyncMiddleware = middleware_or_callable self._async_middleware_list.append(middleware) - elif isinstance(middleware_or_callable, Callable): + elif callable(middleware_or_callable): self._async_middleware_list.append( AsyncCustomMiddleware( app_name=self.name, @@ -773,9 +779,9 @@

Args

if isinstance(callback_id, (str, Pattern)): step = AsyncWorkflowStep( callback_id=callback_id, - edit=edit, - save=save, - execute=execute, + edit=edit, # type: ignore[arg-type] + save=save, # type: ignore[arg-type] + execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, AsyncWorkflowStepBuilder): @@ -808,7 +814,7 @@

Args

func: The function that is supposed to be executed when getting an unhandled error in Bolt app. """ - if not inspect.iscoroutinefunction(func): + if not is_callable_coroutine(func): name = get_name_for_callable(func) raise BoltError(error_listener_function_must_be_coro_func(name)) self._async_listener_runner.listener_error_handler = AsyncCustomListenerErrorHandler( @@ -1406,9 +1412,9 @@

Args

trust_env_in_session=self._async_client.trust_env_in_session, headers=self._async_client.headers, team_id=req.context.team_id, - retry_handlers=self._async_client.retry_handlers.copy() - if self._async_client.retry_handlers is not None - else None, + retry_handlers=( + self._async_client.retry_handlers.copy() if self._async_client.retry_handlers is not None else None + ), ) req.context["client"] = client_per_request @@ -1440,11 +1446,11 @@

Args

value_to_return = functions[0] for func in functions: - if not inspect.iscoroutinefunction(func): + if not is_callable_coroutine(func): name = get_name_for_callable(func) raise BoltError(error_listener_function_must_be_coro_func(name)) - listener_matchers = [ + listener_matchers: List[AsyncListenerMatcher] = [ AsyncCustomListenerMatcher(app_name=self.name, func=f, base_logger=self._base_logger) for f in (matchers or []) ] listener_matchers.insert(0, primary_matcher) @@ -1452,7 +1458,7 @@

Args

for m in middleware or []: if isinstance(m, AsyncMiddleware): listener_middleware.append(m) - elif isinstance(m, Callable) and inspect.iscoroutinefunction(m): + elif callable(m) and is_callable_coroutine(m): listener_middleware.append(AsyncCustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) diff --git a/docs/static/api-docs/slack_bolt/app/async_server.html b/docs/static/api-docs/slack_bolt/app/async_server.html index 7bd3d0f18..8e46004ad 100644 --- a/docs/static/api-docs/slack_bolt/app/async_server.html +++ b/docs/static/api-docs/slack_bolt/app/async_server.html @@ -61,14 +61,14 @@

Args

port: int path: str host: str - bolt_app: "AsyncApp" # type:ignore + bolt_app: "AsyncApp" # type: ignore[name-defined] web_app: web.Application - def __init__( # type:ignore + def __init__( self, port: int, path: str, - app: "AsyncApp", # type:ignore + app: "AsyncApp", # type: ignore[name-defined] host: Optional[str] = None, ): """Standalone AIOHTTP Web Server. @@ -83,7 +83,7 @@

Args

self.port = port self.path = path self.host = host if host is not None else "0.0.0.0" - self.bolt_app: "AsyncApp" = app # type: ignore + self.bolt_app: "AsyncApp" = app # type: ignore[name-defined] self.web_app = web.Application() self._bolt_oauth_flow = self.bolt_app.oauth_flow if self._bolt_oauth_flow: diff --git a/docs/static/api-docs/slack_bolt/app/index.html b/docs/static/api-docs/slack_bolt/app/index.html index 8f3a67ca0..bd6a2b599 100644 --- a/docs/static/api-docs/slack_bolt/app/index.html +++ b/docs/static/api-docs/slack_bolt/app/index.html @@ -257,7 +257,8 @@

Args

listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. """ - signing_secret = signing_secret or os.environ.get("SLACK_SIGNING_SECRET", "") + if signing_secret is None: + signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") token = token or os.environ.get("SLACK_BOT_TOKEN") self._name: str = name or inspect.stack()[1].filename.split(os.path.sep)[-1] @@ -294,7 +295,7 @@

Args

self._before_authorize: Optional[Middleware] = None if before_authorize is not None: - if isinstance(before_authorize, Callable): + if callable(before_authorize): self._before_authorize = CustomMiddleware( app_name=self._name, func=before_authorize, @@ -324,7 +325,7 @@

Args

client_id=settings.client_id if settings is not None else None, client_secret=settings.client_secret if settings is not None else None, logger=self._framework_logger, - bot_only=installation_store_bot_only, + bot_only=installation_store_bot_only or False, client=self._client, # for proxy use cases etc. user_token_resolution=(settings.user_token_resolution if settings is not None else "authed_user"), ) @@ -352,7 +353,8 @@

Args

logger=self._framework_logger, ) self._installation_store = installation_store - self._oauth_flow.settings.installation_store = installation_store + if installation_store is not None: + self._oauth_flow.settings.installation_store = installation_store if self._oauth_flow._client is None: self._oauth_flow._client = self._client @@ -366,11 +368,12 @@

Args

logger=self._framework_logger, ) self._installation_store = installation_store - oauth_settings.installation_store = installation_store + if installation_store is not None: + oauth_settings.installation_store = installation_store self._oauth_flow = OAuthFlow(client=self.client, logger=self.logger, settings=oauth_settings) if self._authorize is None: self._authorize = self._oauth_flow.settings.authorize - self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes + self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes # type: ignore[attr-defined] # noqa: E501 if (self._installation_store is not None or self._authorize is not None) and self._token is not None: self._token = None @@ -383,7 +386,7 @@

Args

if app_bot_only != oauth_flow_bot_only: self.logger.warning(warning_bot_only_conflicts()) self._oauth_flow.settings.installation_store_bot_only = app_bot_only - self._authorize.bot_only = app_bot_only + self._authorize.bot_only = app_bot_only # type: ignore[union-attr] self._tokens_revocation_listeners: Optional[TokenRevocationListeners] = None if self._installation_store is not None: @@ -412,7 +415,7 @@

Args

executor=listener_executor, ), ) - self._middleware_error_handler = DefaultMiddlewareErrorHandler( + self._middleware_error_handler: MiddlewareErrorHandler = DefaultMiddlewareErrorHandler( logger=self._framework_logger, ) @@ -479,7 +482,7 @@

Args

) else: raise BoltError(error_token_required()) - else: + elif self._authorize is not None: self._middleware_list.append( MultiTeamsAuthorization( authorize=self._authorize, @@ -488,6 +491,9 @@

Args

user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) + else: + raise BoltError(error_oauth_flow_or_authorize_required()) + if ignoring_self_events_enabled is True: self._middleware_list.append(IgnoringSelfEvents(base_logger=self._base_logger)) if url_verification_enabled is True: @@ -591,7 +597,7 @@

Args

middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(debug_applying_middleware(middleware.name)) - resp = middleware.process(req=req, resp=resp, next=middleware_next) + resp = middleware.process(req=req, resp=resp, next=middleware_next) # type: ignore[arg-type] if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack @@ -618,9 +624,11 @@

Args

for listener in self._listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) - if listener.matches(req=req, resp=resp): + if listener.matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) + middleware_resp, next_was_not_called = listener.run_middleware( + req=req, resp=resp # type: ignore[arg-type] + ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: @@ -642,7 +650,7 @@

Args

self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = self._listener_runner.run( request=req, - response=resp, + response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) @@ -712,7 +720,7 @@

Args

if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) - elif isinstance(middleware_or_callable, Callable): + elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( app_name=self.name, @@ -781,9 +789,9 @@

Args

if isinstance(callback_id, (str, Pattern)): step = WorkflowStep( callback_id=callback_id, - edit=edit, - save=save, - execute=execute, + edit=edit, # type: ignore[arg-type] + save=save, # type: ignore[arg-type] + execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, WorkflowStepBuilder): @@ -1433,7 +1441,7 @@

Args

# the registration should return the original function. value_to_return = functions[0] - listener_matchers = [ + listener_matchers: List[ListenerMatcher] = [ CustomListenerMatcher(app_name=self.name, func=f, base_logger=self._base_logger) for f in (matchers or []) ] listener_matchers.insert(0, primary_matcher) @@ -1441,7 +1449,7 @@

Args

for m in middleware or []: if isinstance(m, Middleware): listener_middleware.append(m) - elif isinstance(m, Callable): + elif callable(m): listener_middleware.append(CustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) diff --git a/docs/static/api-docs/slack_bolt/async_app.html b/docs/static/api-docs/slack_bolt/async_app.html index cb7e64187..39aaed50c 100644 --- a/docs/static/api-docs/slack_bolt/async_app.html +++ b/docs/static/api-docs/slack_bolt/async_app.html @@ -318,7 +318,8 @@

Args

oauth_flow: Instantiated `slack_bolt.oauth.AsyncOAuthFlow`. This is always prioritized over oauth_settings. verification_token: Deprecated verification mechanism. This can used only for ssl_check requests. """ - signing_secret = signing_secret or os.environ.get("SLACK_SIGNING_SECRET", "") + if signing_secret is None: + signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") token = token or os.environ.get("SLACK_BOT_TOKEN") self._name: str = name or inspect.stack()[1].filename.split(os.path.sep)[-1] @@ -354,7 +355,7 @@

Args

self._async_before_authorize: Optional[AsyncMiddleware] = None if before_authorize is not None: - if isinstance(before_authorize, Callable): + if callable(before_authorize): self._async_before_authorize = AsyncCustomMiddleware( app_name=self._name, func=before_authorize, @@ -384,7 +385,7 @@

Args

client_id=settings.client_id if settings is not None else None, client_secret=settings.client_secret if settings is not None else None, logger=self._framework_logger, - bot_only=installation_store_bot_only, + bot_only=installation_store_bot_only or False, client=self._async_client, # for proxy use cases etc. user_token_resolution=(settings.user_token_resolution if settings is not None else "authed_user"), ) @@ -415,7 +416,8 @@

Args

logger=self._framework_logger, ) self._async_installation_store = installation_store - self._async_oauth_flow.settings.installation_store = installation_store + if installation_store is not None: + self._async_oauth_flow.settings.installation_store = installation_store if self._async_oauth_flow._async_client is None: self._async_oauth_flow._async_client = self._async_client @@ -432,12 +434,13 @@

Args

logger=self._framework_logger, ) self._async_installation_store = installation_store - oauth_settings.installation_store = installation_store + if installation_store is not None: + oauth_settings.installation_store = installation_store self._async_oauth_flow = AsyncOAuthFlow(client=self._async_client, logger=self.logger, settings=oauth_settings) if self._async_authorize is None: self._async_authorize = self._async_oauth_flow.settings.authorize - self._async_authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes + self._async_authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes # type: ignore[attr-defined] # noqa: E501 if (self._async_installation_store is not None or self._async_authorize is not None) and self._token is not None: self._token = None @@ -450,7 +453,7 @@

Args

if app_bot_only != oauth_flow_bot_only: self.logger.warning(warning_bot_only_conflicts()) self._async_oauth_flow.settings.installation_store_bot_only = app_bot_only - self._async_authorize.bot_only = app_bot_only + self._async_authorize.bot_only = app_bot_only # type: ignore[union-attr] self._async_tokens_revocation_listeners: Optional[AsyncTokenRevocationListeners] = None if self._async_installation_store is not None: @@ -474,7 +477,7 @@

Args

logger=self._framework_logger, ), ) - self._async_middleware_error_handler = AsyncDefaultMiddlewareErrorHandler( + self._async_middleware_error_handler: AsyncMiddlewareErrorHandler = AsyncDefaultMiddlewareErrorHandler( logger=self._framework_logger, ) @@ -533,7 +536,7 @@

Args

) else: raise BoltError(error_token_required()) - else: + elif self._async_authorize is not None: self._async_middleware_list.append( AsyncMultiTeamsAuthorization( authorize=self._async_authorize, @@ -542,6 +545,8 @@

Args

user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) + else: + raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: self._async_middleware_list.append(AsyncIgnoringSelfEvents(base_logger=self._base_logger)) @@ -675,7 +680,9 @@

Args

middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(f"Applying {middleware.name}") - resp = await middleware.async_process(req=req, resp=resp, next=async_middleware_next) + resp = await middleware.async_process( + req=req, resp=resp, next=async_middleware_next # type: ignore[arg-type] + ) if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack @@ -702,12 +709,11 @@

Args

for listener in self._async_listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) - if await listener.async_matches(req=req, resp=resp): + if await listener.async_matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - ( - middleware_resp, - next_was_not_called, - ) = await listener.run_async_middleware(req=req, resp=resp) + (middleware_resp, next_was_not_called) = await listener.run_async_middleware( + req=req, resp=resp # type: ignore[arg-type] + ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: @@ -729,7 +735,7 @@

Args

self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = await self._async_listener_runner.run( request=req, - response=resp, + response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) @@ -796,7 +802,7 @@

Args

if isinstance(middleware_or_callable, AsyncMiddleware): middleware: AsyncMiddleware = middleware_or_callable self._async_middleware_list.append(middleware) - elif isinstance(middleware_or_callable, Callable): + elif callable(middleware_or_callable): self._async_middleware_list.append( AsyncCustomMiddleware( app_name=self.name, @@ -864,9 +870,9 @@

Args

if isinstance(callback_id, (str, Pattern)): step = AsyncWorkflowStep( callback_id=callback_id, - edit=edit, - save=save, - execute=execute, + edit=edit, # type: ignore[arg-type] + save=save, # type: ignore[arg-type] + execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, AsyncWorkflowStepBuilder): @@ -899,7 +905,7 @@

Args

func: The function that is supposed to be executed when getting an unhandled error in Bolt app. """ - if not inspect.iscoroutinefunction(func): + if not is_callable_coroutine(func): name = get_name_for_callable(func) raise BoltError(error_listener_function_must_be_coro_func(name)) self._async_listener_runner.listener_error_handler = AsyncCustomListenerErrorHandler( @@ -1497,9 +1503,9 @@

Args

trust_env_in_session=self._async_client.trust_env_in_session, headers=self._async_client.headers, team_id=req.context.team_id, - retry_handlers=self._async_client.retry_handlers.copy() - if self._async_client.retry_handlers is not None - else None, + retry_handlers=( + self._async_client.retry_handlers.copy() if self._async_client.retry_handlers is not None else None + ), ) req.context["client"] = client_per_request @@ -1531,11 +1537,11 @@

Args

value_to_return = functions[0] for func in functions: - if not inspect.iscoroutinefunction(func): + if not is_callable_coroutine(func): name = get_name_for_callable(func) raise BoltError(error_listener_function_must_be_coro_func(name)) - listener_matchers = [ + listener_matchers: List[AsyncListenerMatcher] = [ AsyncCustomListenerMatcher(app_name=self.name, func=f, base_logger=self._base_logger) for f in (matchers or []) ] listener_matchers.insert(0, primary_matcher) @@ -1543,7 +1549,7 @@

Args

for m in middleware or []: if isinstance(m, AsyncMiddleware): listener_middleware.append(m) - elif isinstance(m, Callable) and inspect.iscoroutinefunction(m): + elif callable(m) and is_callable_coroutine(m): listener_middleware.append(AsyncCustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) @@ -2299,8 +2305,8 @@

Args

if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] @@ -2325,7 +2331,9 @@

Args

Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = AsyncComplete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @property @@ -2349,7 +2357,9 @@

Args

Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = AsyncFail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"]

Ancestors

@@ -2492,7 +2502,9 @@

Returns

Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = AsyncComplete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"]
@@ -2539,7 +2551,9 @@

Returns

Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = AsyncFail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"]
@@ -2584,8 +2598,8 @@

Returns

if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] @@ -2839,7 +2853,7 @@

Methods

return await self.func( **build_async_required_kwargs( logger=self.logger, - required_arg_names=self.arg_names, + required_arg_names=self.arg_names, # type: ignore[arg-type] request=req, response=resp, this_func=self.func, @@ -2928,7 +2942,7 @@

Inherited members

async def _next(): middleware_state["next_called"] = True - resp = await m.async_process(req=req, resp=resp, next=_next) + resp = await m.async_process(req=req, resp=resp, next=_next) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) @@ -3062,7 +3076,7 @@

Returns

text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): message = _build_message( - text=text, + text=text, # type: ignore[arg-type] blocks=blocks, attachments=attachments, response_type=response_type, @@ -3145,8 +3159,8 @@

Class variables

text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response - return await self.client.chat_postMessage( - channel=channel or self.channel, + return await self.client.chat_postMessage( # type: ignore[union-attr] + channel=channel or self.channel, # type: ignore[arg-type] text=text, blocks=blocks, attachments=attachments, @@ -3168,7 +3182,7 @@

Class variables

message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel - return await self.client.chat_postMessage(**message) + return await self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize.html b/docs/static/api-docs/slack_bolt/authorization/async_authorize.html index dd03ecf71..4a9b022b7 100644 --- a/docs/static/api-docs/slack_bolt/authorization/async_authorize.html +++ b/docs/static/api-docs/slack_bolt/authorization/async_authorize.html @@ -127,9 +127,7 @@

Subclasses

if k not in all_available_args: all_available_args[k] = v - kwargs: Dict[str, Any] = { # type: ignore - k: v for k, v in all_available_args.items() if k in self.arg_names # type: ignore - } + kwargs: Dict[str, Any] = {k: v for k, v in all_available_args.items() if k in self.arg_names} found_arg_names = kwargs.keys() for name in self.arg_names: if name not in found_arg_names: @@ -237,8 +235,8 @@

Ancestors

bot_token: Optional[str] = None user_token: Optional[str] = None - bot_scopes: Optional[List[str]] = None - user_scopes: Optional[List[str]] = None + bot_scopes: Optional[Sequence[str]] = None + user_scopes: Optional[Sequence[str]] = None latest_bot_installation: Optional[Installation] = None this_user_installation: Optional[Installation] = None @@ -364,14 +362,14 @@

Ancestors

# Token rotation if self.token_rotator is None: raise BoltError(self._config_error_message) - refreshed = await self.token_rotator.perform_bot_token_rotation( + refreshed_bot = await self.token_rotator.perform_bot_token_rotation( bot=bot, minutes_before_expiration=self.token_rotation_expiration_minutes, ) - if refreshed is not None: - await self.installation_store.async_save_bot(refreshed) - bot_token = refreshed.bot_token - bot_scopes = refreshed.bot_scopes + if refreshed_bot is not None: + await self.installation_store.async_save_bot(refreshed_bot) + bot_token = refreshed_bot.bot_token + bot_scopes = refreshed_bot.bot_scopes except SlackTokenRotationError as rotation_error: # When token rotation fails, it is usually unrecoverable @@ -394,10 +392,10 @@

Ancestors

return self.authorize_result_cache[token] try: - auth_test_api_response = await context.client.auth_test(token=token) + auth_test_api_response = await context.client.auth_test(token=token) # type: ignore[union-attr] user_auth_test_response = None if user_token is not None and token != user_token: - user_auth_test_response = await context.client.auth_test(token=user_token) + user_auth_test_response = await context.client.auth_test(token=user_token) # type: ignore[union-attr] authorize_result = AuthorizeResult.from_auth_test_response( auth_test_response=auth_test_api_response, user_auth_test_response=user_auth_test_response, diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html b/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html index e93200864..763fcad3d 100644 --- a/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html +++ b/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html @@ -82,7 +82,7 @@

Args

""" self.context = context self.logger = context.logger - self.client = context.client + self.client = context.client # type: ignore[assignment] self.enterprise_id = enterprise_id self.team_id = team_id self.user_id = user_id diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize.html b/docs/static/api-docs/slack_bolt/authorization/authorize.html index 79812f72e..5534eb3ac 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize.html @@ -132,9 +132,7 @@

Subclasses

if k not in all_available_args: all_available_args[k] = v - kwargs: Dict[str, Any] = { # type: ignore - k: v for k, v in all_available_args.items() if k in self.arg_names # type: ignore - } + kwargs: Dict[str, Any] = {k: v for k, v in all_available_args.items() if k in self.arg_names} found_arg_names = kwargs.keys() for name in self.arg_names: if name not in found_arg_names: @@ -237,8 +235,8 @@

Ancestors

bot_token: Optional[str] = None user_token: Optional[str] = None - bot_scopes: Optional[List[str]] = None - user_scopes: Optional[List[str]] = None + bot_scopes: Optional[Sequence[str]] = None + user_scopes: Optional[Sequence[str]] = None latest_bot_installation: Optional[Installation] = None this_user_installation: Optional[Installation] = None @@ -362,14 +360,14 @@

Ancestors

# Token rotation if self.token_rotator is None: raise BoltError(self._config_error_message) - refreshed = self.token_rotator.perform_bot_token_rotation( + refreshed_bot = self.token_rotator.perform_bot_token_rotation( bot=bot, minutes_before_expiration=self.token_rotation_expiration_minutes, ) - if refreshed is not None: - self.installation_store.save_bot(refreshed) - bot_token = refreshed.bot_token - bot_scopes = refreshed.bot_scopes + if refreshed_bot is not None: + self.installation_store.save_bot(refreshed_bot) + bot_token = refreshed_bot.bot_token + bot_scopes = refreshed_bot.bot_scopes except SlackTokenRotationError as rotation_error: # When token rotation fails, it is usually unrecoverable @@ -392,10 +390,10 @@

Ancestors

return self.authorize_result_cache[token] try: - auth_test_api_response = context.client.auth_test(token=token) + auth_test_api_response = context.client.auth_test(token=token) # type: ignore[union-attr] user_auth_test_response = None if user_token is not None and token != user_token: - user_auth_test_response = context.client.auth_test(token=user_token) + user_auth_test_response = context.client.auth_test(token=user_token) # type: ignore[union-attr] authorize_result = AuthorizeResult.from_auth_test_response( auth_test_response=auth_test_api_response, user_auth_test_response=user_auth_test_response, diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize_args.html b/docs/static/api-docs/slack_bolt/authorization/authorize_args.html index fec8531bf..bd32c1389 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize_args.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize_args.html @@ -82,7 +82,7 @@

Args

""" self.context = context self.logger = context.logger - self.client = context.client + self.client = context.client # type: ignore[assignment] self.enterprise_id = enterprise_id self.team_id = team_id self.user_id = user_id diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize_result.html b/docs/static/api-docs/slack_bolt/authorization/authorize_result.html index 019c69d12..3c5ae265f 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize_result.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize_result.html @@ -37,7 +37,7 @@

Classes

class AuthorizeResult -(*, enterprise_id: Optional[str], team_id: Optional[str], team: Optional[str] = None, url: Optional[str] = None, bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, bot_scopes: Union[List[str], str, ForwardRef(None)] = None, user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, user_scopes: Union[List[str], str, ForwardRef(None)] = None) +(*, enterprise_id: Optional[str], team_id: Optional[str], team: Optional[str] = None, url: Optional[str] = None, bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None)

Authorize function call result

@@ -83,12 +83,12 @@

Args

bot_id: Optional[str] bot_user_id: Optional[str] bot_token: Optional[str] - bot_scopes: Optional[List[str]] # since v1.17 + bot_scopes: Optional[Sequence[str]] # since v1.17 user_id: Optional[str] user: Optional[str] # since v1.18 user_token: Optional[str] - user_scopes: Optional[List[str]] # since v1.17 + user_scopes: Optional[Sequence[str]] # since v1.17 def __init__( self, @@ -101,12 +101,12 @@

Args

bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, - bot_scopes: Optional[Union[List[str], str]] = None, + bot_scopes: Optional[Union[Sequence[str], str]] = None, # user user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, - user_scopes: Optional[Union[List[str], str]] = None, + user_scopes: Optional[Union[Sequence[str], str]] = None, ): """ Args: @@ -133,14 +133,14 @@

Args

self["bot_token"] = self.bot_token = bot_token if bot_scopes is not None and isinstance(bot_scopes, str): bot_scopes = [scope.strip() for scope in bot_scopes.split(",")] - self["bot_scopes"] = self.bot_scopes = bot_scopes # type: ignore + self["bot_scopes"] = self.bot_scopes = bot_scopes # user self["user_id"] = self.user_id = user_id self["user"] = self.user = user self["user_token"] = self.user_token = user_token if user_scopes is not None and isinstance(user_scopes, str): user_scopes = [scope.strip() for scope in user_scopes.split(",")] - self["user_scopes"] = self.user_scopes = user_scopes # type: ignore + self["user_scopes"] = self.user_scopes = user_scopes @classmethod def from_auth_test_response( @@ -148,21 +148,19 @@

Args

*, bot_token: Optional[str] = None, user_token: Optional[str] = None, - bot_scopes: Optional[Union[List[str], str]] = None, - user_scopes: Optional[Union[List[str], str]] = None, - auth_test_response: SlackResponse, - user_auth_test_response: Optional[SlackResponse] = None, + bot_scopes: Optional[Union[Sequence[str], str]] = None, + user_scopes: Optional[Union[Sequence[str], str]] = None, + auth_test_response: Union[SlackResponse, "AsyncSlackResponse"], # type: ignore[name-defined] + user_auth_test_response: Optional[Union[SlackResponse, "AsyncSlackResponse"]] = None, # type: ignore[name-defined] ) -> "AuthorizeResult": - bot_user_id: Optional[str] = ( # type:ignore + bot_user_id: Optional[str] = ( auth_test_response.get("user_id") if auth_test_response.get("bot_id") is not None else None ) - user_id: Optional[str] = ( # type:ignore - auth_test_response.get("user_id") if auth_test_response.get("bot_id") is None else None - ) - user_name = auth_test_response.get("user") + user_id: Optional[str] = auth_test_response.get("user_id") if auth_test_response.get("bot_id") is None else None + user_name: Optional[str] = auth_test_response.get("user") if user_id is None and user_auth_test_response is not None: - user_id: Optional[str] = user_auth_test_response.get("user_id") # type:ignore - user_name: Optional[str] = user_auth_test_response.get("user") # type:ignore + user_id = user_auth_test_response.get("user_id") + user_name = user_auth_test_response.get("user") return AuthorizeResult( enterprise_id=auth_test_response.get("enterprise_id"), @@ -189,7 +187,7 @@

Class variables

-
var bot_scopes : Optional[List[str]]
+
var bot_scopes : Optional[Sequence[str]]
@@ -225,7 +223,7 @@

Class variables

-
var user_scopes : Optional[List[str]]
+
var user_scopes : Optional[Sequence[str]]
@@ -237,7 +235,7 @@

Class variables

Static methods

-def from_auth_test_response(*, bot_token: Optional[str] = None, user_token: Optional[str] = None, bot_scopes: Union[List[str], str, ForwardRef(None)] = None, user_scopes: Union[List[str], str, ForwardRef(None)] = None, auth_test_response: slack_sdk.web.slack_response.SlackResponse, user_auth_test_response: Optional[slack_sdk.web.slack_response.SlackResponse] = None) ‑> AuthorizeResult +def from_auth_test_response(*, bot_token: Optional[str] = None, user_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse')], user_auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse'), ForwardRef(None)] = None)
diff --git a/docs/static/api-docs/slack_bolt/authorization/index.html b/docs/static/api-docs/slack_bolt/authorization/index.html index 2263ebb4f..eafddd773 100644 --- a/docs/static/api-docs/slack_bolt/authorization/index.html +++ b/docs/static/api-docs/slack_bolt/authorization/index.html @@ -64,7 +64,7 @@

Classes

class AuthorizeResult -(*, enterprise_id: Optional[str], team_id: Optional[str], team: Optional[str] = None, url: Optional[str] = None, bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, bot_scopes: Union[List[str], str, ForwardRef(None)] = None, user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, user_scopes: Union[List[str], str, ForwardRef(None)] = None) +(*, enterprise_id: Optional[str], team_id: Optional[str], team: Optional[str] = None, url: Optional[str] = None, bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None)

Authorize function call result

@@ -110,12 +110,12 @@

Args

bot_id: Optional[str] bot_user_id: Optional[str] bot_token: Optional[str] - bot_scopes: Optional[List[str]] # since v1.17 + bot_scopes: Optional[Sequence[str]] # since v1.17 user_id: Optional[str] user: Optional[str] # since v1.18 user_token: Optional[str] - user_scopes: Optional[List[str]] # since v1.17 + user_scopes: Optional[Sequence[str]] # since v1.17 def __init__( self, @@ -128,12 +128,12 @@

Args

bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, - bot_scopes: Optional[Union[List[str], str]] = None, + bot_scopes: Optional[Union[Sequence[str], str]] = None, # user user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, - user_scopes: Optional[Union[List[str], str]] = None, + user_scopes: Optional[Union[Sequence[str], str]] = None, ): """ Args: @@ -160,14 +160,14 @@

Args

self["bot_token"] = self.bot_token = bot_token if bot_scopes is not None and isinstance(bot_scopes, str): bot_scopes = [scope.strip() for scope in bot_scopes.split(",")] - self["bot_scopes"] = self.bot_scopes = bot_scopes # type: ignore + self["bot_scopes"] = self.bot_scopes = bot_scopes # user self["user_id"] = self.user_id = user_id self["user"] = self.user = user self["user_token"] = self.user_token = user_token if user_scopes is not None and isinstance(user_scopes, str): user_scopes = [scope.strip() for scope in user_scopes.split(",")] - self["user_scopes"] = self.user_scopes = user_scopes # type: ignore + self["user_scopes"] = self.user_scopes = user_scopes @classmethod def from_auth_test_response( @@ -175,21 +175,19 @@

Args

*, bot_token: Optional[str] = None, user_token: Optional[str] = None, - bot_scopes: Optional[Union[List[str], str]] = None, - user_scopes: Optional[Union[List[str], str]] = None, - auth_test_response: SlackResponse, - user_auth_test_response: Optional[SlackResponse] = None, + bot_scopes: Optional[Union[Sequence[str], str]] = None, + user_scopes: Optional[Union[Sequence[str], str]] = None, + auth_test_response: Union[SlackResponse, "AsyncSlackResponse"], # type: ignore[name-defined] + user_auth_test_response: Optional[Union[SlackResponse, "AsyncSlackResponse"]] = None, # type: ignore[name-defined] ) -> "AuthorizeResult": - bot_user_id: Optional[str] = ( # type:ignore + bot_user_id: Optional[str] = ( auth_test_response.get("user_id") if auth_test_response.get("bot_id") is not None else None ) - user_id: Optional[str] = ( # type:ignore - auth_test_response.get("user_id") if auth_test_response.get("bot_id") is None else None - ) - user_name = auth_test_response.get("user") + user_id: Optional[str] = auth_test_response.get("user_id") if auth_test_response.get("bot_id") is None else None + user_name: Optional[str] = auth_test_response.get("user") if user_id is None and user_auth_test_response is not None: - user_id: Optional[str] = user_auth_test_response.get("user_id") # type:ignore - user_name: Optional[str] = user_auth_test_response.get("user") # type:ignore + user_id = user_auth_test_response.get("user_id") + user_name = user_auth_test_response.get("user") return AuthorizeResult( enterprise_id=auth_test_response.get("enterprise_id"), @@ -216,7 +214,7 @@

Class variables

-
var bot_scopes : Optional[List[str]]
+
var bot_scopes : Optional[Sequence[str]]
@@ -252,7 +250,7 @@

Class variables

-
var user_scopes : Optional[List[str]]
+
var user_scopes : Optional[Sequence[str]]
@@ -264,7 +262,7 @@

Class variables

Static methods

-def from_auth_test_response(*, bot_token: Optional[str] = None, user_token: Optional[str] = None, bot_scopes: Union[List[str], str, ForwardRef(None)] = None, user_scopes: Union[List[str], str, ForwardRef(None)] = None, auth_test_response: slack_sdk.web.slack_response.SlackResponse, user_auth_test_response: Optional[slack_sdk.web.slack_response.SlackResponse] = None) ‑> AuthorizeResult +def from_auth_test_response(*, bot_token: Optional[str] = None, user_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse')], user_auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse'), ForwardRef(None)] = None)
diff --git a/docs/static/api-docs/slack_bolt/context/async_context.html b/docs/static/api-docs/slack_bolt/context/async_context.html index f7f5d35bd..0bd0311bc 100644 --- a/docs/static/api-docs/slack_bolt/context/async_context.html +++ b/docs/static/api-docs/slack_bolt/context/async_context.html @@ -154,8 +154,8 @@

Classes

if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] @@ -180,7 +180,9 @@

Classes

Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = AsyncComplete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @property @@ -204,7 +206,9 @@

Classes

Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = AsyncFail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"]

Ancestors

@@ -347,7 +351,9 @@

Returns

Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = AsyncComplete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"]
@@ -394,7 +400,9 @@

Returns

Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = AsyncFail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"]
@@ -439,8 +447,8 @@

Returns

if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] diff --git a/docs/static/api-docs/slack_bolt/context/context.html b/docs/static/api-docs/slack_bolt/context/context.html index a7ece7760..32fb34b86 100644 --- a/docs/static/api-docs/slack_bolt/context/context.html +++ b/docs/static/api-docs/slack_bolt/context/context.html @@ -155,8 +155,8 @@

Classes

if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] @@ -181,7 +181,9 @@

Classes

Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = Complete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @property @@ -205,7 +207,9 @@

Classes

Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = Fail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"]

Ancestors

@@ -348,7 +352,9 @@

Returns

Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = Complete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @@ -395,7 +401,9 @@

Returns

Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = Fail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"] @@ -440,8 +448,8 @@

Returns

if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] diff --git a/docs/static/api-docs/slack_bolt/context/index.html b/docs/static/api-docs/slack_bolt/context/index.html index c7cf9af71..341403877 100644 --- a/docs/static/api-docs/slack_bolt/context/index.html +++ b/docs/static/api-docs/slack_bolt/context/index.html @@ -195,8 +195,8 @@

Classes

if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] @@ -221,7 +221,9 @@

Classes

Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = Complete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @property @@ -245,7 +247,9 @@

Classes

Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = Fail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"]

Ancestors

@@ -388,7 +392,9 @@

Returns

Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = Complete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @@ -435,7 +441,9 @@

Returns

Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = Fail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"] @@ -480,8 +488,8 @@

Returns

if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] diff --git a/docs/static/api-docs/slack_bolt/context/respond/async_respond.html b/docs/static/api-docs/slack_bolt/context/respond/async_respond.html index 301746937..56a426ba8 100644 --- a/docs/static/api-docs/slack_bolt/context/respond/async_respond.html +++ b/docs/static/api-docs/slack_bolt/context/respond/async_respond.html @@ -83,7 +83,7 @@

Classes

text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): message = _build_message( - text=text, + text=text, # type: ignore[arg-type] blocks=blocks, attachments=attachments, response_type=response_type, diff --git a/docs/static/api-docs/slack_bolt/context/say/async_say.html b/docs/static/api-docs/slack_bolt/context/say/async_say.html index db3dc08fa..47577bddd 100644 --- a/docs/static/api-docs/slack_bolt/context/say/async_say.html +++ b/docs/static/api-docs/slack_bolt/context/say/async_say.html @@ -81,8 +81,8 @@

Classes

text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response - return await self.client.chat_postMessage( - channel=channel or self.channel, + return await self.client.chat_postMessage( # type: ignore[union-attr] + channel=channel or self.channel, # type: ignore[arg-type] text=text, blocks=blocks, attachments=attachments, @@ -104,7 +104,7 @@

Classes

message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel - return await self.client.chat_postMessage(**message) + return await self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: diff --git a/docs/static/api-docs/slack_bolt/context/say/index.html b/docs/static/api-docs/slack_bolt/context/say/index.html index 818ec6536..f6fd5bb4d 100644 --- a/docs/static/api-docs/slack_bolt/context/say/index.html +++ b/docs/static/api-docs/slack_bolt/context/say/index.html @@ -96,8 +96,8 @@

Classes

text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response - return self.client.chat_postMessage( - channel=channel or self.channel, + return self.client.chat_postMessage( # type: ignore[union-attr] + channel=channel or self.channel, # type: ignore[arg-type] text=text, blocks=blocks, attachments=attachments, @@ -119,7 +119,7 @@

Classes

message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel - return self.client.chat_postMessage(**message) + return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: diff --git a/docs/static/api-docs/slack_bolt/context/say/say.html b/docs/static/api-docs/slack_bolt/context/say/say.html index c051ed1b6..bfbc1a677 100644 --- a/docs/static/api-docs/slack_bolt/context/say/say.html +++ b/docs/static/api-docs/slack_bolt/context/say/say.html @@ -81,8 +81,8 @@

Classes

text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response - return self.client.chat_postMessage( - channel=channel or self.channel, + return self.client.chat_postMessage( # type: ignore[union-attr] + channel=channel or self.channel, # type: ignore[arg-type] text=text, blocks=blocks, attachments=attachments, @@ -104,7 +104,7 @@

Classes

message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel - return self.client.chat_postMessage(**message) + return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: diff --git a/docs/static/api-docs/slack_bolt/error/index.html b/docs/static/api-docs/slack_bolt/error/index.html index 2b6a31bb5..2b3e8b043 100644 --- a/docs/static/api-docs/slack_bolt/error/index.html +++ b/docs/static/api-docs/slack_bolt/error/index.html @@ -70,16 +70,16 @@

Subclasses

Expand source code
class BoltUnhandledRequestError(BoltError):
-    request: "BoltRequest"  # type: ignore
+    request: "BoltRequest"  # type: ignore[name-defined]
     body: dict
-    current_response: Optional["BoltResponse"]  # type: ignore
+    current_response: Optional["BoltResponse"]  # type: ignore[name-defined]
     last_global_middleware_name: Optional[str]
 
-    def __init__(  # type: ignore
+    def __init__(
         self,
         *,
-        request: Union["BoltRequest", "AsyncBoltRequest"],  # type: ignore
-        current_response: Optional["BoltResponse"],  # type: ignore
+        request: Union["BoltRequest", "AsyncBoltRequest"],  # type: ignore[name-defined]
+        current_response: Optional["BoltResponse"],  # type: ignore[name-defined]
         last_global_middleware_name: Optional[str] = None,
     ):
         self.request = request
diff --git a/docs/static/api-docs/slack_bolt/index.html b/docs/static/api-docs/slack_bolt/index.html
index e58083201..ff34a124d 100644
--- a/docs/static/api-docs/slack_bolt/index.html
+++ b/docs/static/api-docs/slack_bolt/index.html
@@ -378,7 +378,8 @@ 

Args

listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. """ - signing_secret = signing_secret or os.environ.get("SLACK_SIGNING_SECRET", "") + if signing_secret is None: + signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") token = token or os.environ.get("SLACK_BOT_TOKEN") self._name: str = name or inspect.stack()[1].filename.split(os.path.sep)[-1] @@ -415,7 +416,7 @@

Args

self._before_authorize: Optional[Middleware] = None if before_authorize is not None: - if isinstance(before_authorize, Callable): + if callable(before_authorize): self._before_authorize = CustomMiddleware( app_name=self._name, func=before_authorize, @@ -445,7 +446,7 @@

Args

client_id=settings.client_id if settings is not None else None, client_secret=settings.client_secret if settings is not None else None, logger=self._framework_logger, - bot_only=installation_store_bot_only, + bot_only=installation_store_bot_only or False, client=self._client, # for proxy use cases etc. user_token_resolution=(settings.user_token_resolution if settings is not None else "authed_user"), ) @@ -473,7 +474,8 @@

Args

logger=self._framework_logger, ) self._installation_store = installation_store - self._oauth_flow.settings.installation_store = installation_store + if installation_store is not None: + self._oauth_flow.settings.installation_store = installation_store if self._oauth_flow._client is None: self._oauth_flow._client = self._client @@ -487,11 +489,12 @@

Args

logger=self._framework_logger, ) self._installation_store = installation_store - oauth_settings.installation_store = installation_store + if installation_store is not None: + oauth_settings.installation_store = installation_store self._oauth_flow = OAuthFlow(client=self.client, logger=self.logger, settings=oauth_settings) if self._authorize is None: self._authorize = self._oauth_flow.settings.authorize - self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes + self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes # type: ignore[attr-defined] # noqa: E501 if (self._installation_store is not None or self._authorize is not None) and self._token is not None: self._token = None @@ -504,7 +507,7 @@

Args

if app_bot_only != oauth_flow_bot_only: self.logger.warning(warning_bot_only_conflicts()) self._oauth_flow.settings.installation_store_bot_only = app_bot_only - self._authorize.bot_only = app_bot_only + self._authorize.bot_only = app_bot_only # type: ignore[union-attr] self._tokens_revocation_listeners: Optional[TokenRevocationListeners] = None if self._installation_store is not None: @@ -533,7 +536,7 @@

Args

executor=listener_executor, ), ) - self._middleware_error_handler = DefaultMiddlewareErrorHandler( + self._middleware_error_handler: MiddlewareErrorHandler = DefaultMiddlewareErrorHandler( logger=self._framework_logger, ) @@ -600,7 +603,7 @@

Args

) else: raise BoltError(error_token_required()) - else: + elif self._authorize is not None: self._middleware_list.append( MultiTeamsAuthorization( authorize=self._authorize, @@ -609,6 +612,9 @@

Args

user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) + else: + raise BoltError(error_oauth_flow_or_authorize_required()) + if ignoring_self_events_enabled is True: self._middleware_list.append(IgnoringSelfEvents(base_logger=self._base_logger)) if url_verification_enabled is True: @@ -712,7 +718,7 @@

Args

middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(debug_applying_middleware(middleware.name)) - resp = middleware.process(req=req, resp=resp, next=middleware_next) + resp = middleware.process(req=req, resp=resp, next=middleware_next) # type: ignore[arg-type] if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack @@ -739,9 +745,11 @@

Args

for listener in self._listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) - if listener.matches(req=req, resp=resp): + if listener.matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) + middleware_resp, next_was_not_called = listener.run_middleware( + req=req, resp=resp # type: ignore[arg-type] + ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: @@ -763,7 +771,7 @@

Args

self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = self._listener_runner.run( request=req, - response=resp, + response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) @@ -833,7 +841,7 @@

Args

if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) - elif isinstance(middleware_or_callable, Callable): + elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( app_name=self.name, @@ -902,9 +910,9 @@

Args

if isinstance(callback_id, (str, Pattern)): step = WorkflowStep( callback_id=callback_id, - edit=edit, - save=save, - execute=execute, + edit=edit, # type: ignore[arg-type] + save=save, # type: ignore[arg-type] + execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, WorkflowStepBuilder): @@ -1554,7 +1562,7 @@

Args

# the registration should return the original function. value_to_return = functions[0] - listener_matchers = [ + listener_matchers: List[ListenerMatcher] = [ CustomListenerMatcher(app_name=self.name, func=f, base_logger=self._base_logger) for f in (matchers or []) ] listener_matchers.insert(0, primary_matcher) @@ -1562,7 +1570,7 @@

Args

for m in middleware or []: if isinstance(m, Middleware): listener_middleware.append(m) - elif isinstance(m, Callable): + elif callable(m): listener_middleware.append(CustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) @@ -2538,8 +2546,8 @@

Class variables

if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] @@ -2564,7 +2572,9 @@

Class variables

Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = Complete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @property @@ -2588,7 +2598,9 @@

Class variables

Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = Fail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"]

Ancestors

@@ -2731,7 +2743,9 @@

Returns

Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) + self["complete"] = Complete( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["complete"] @@ -2778,7 +2792,9 @@

Returns

Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + self["fail"] = Fail( + client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + ) return self["fail"] @@ -2823,8 +2839,8 @@

Returns

if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, - ssl=self.client.ssl, + proxy=self.client.proxy, # type: ignore[union-attr] + ssl=self.client.ssl, # type: ignore[union-attr] ) return self["respond"] @@ -3226,7 +3242,7 @@

Class variables

class CustomListenerMatcher(ListenerMatcher):
     app_name: str
     func: Callable[..., bool]
-    arg_names: Sequence[str]
+    arg_names: MutableSequence[str]
     logger: Logger
 
     def __init__(self, *, app_name: str, func: Callable[..., bool], base_logger: Optional[Logger] = None):
@@ -3256,7 +3272,7 @@ 

Class variables

-
var arg_names : Sequence[str]
+
var arg_names : MutableSequence[str]
@@ -3340,7 +3356,7 @@

Class variables

class Listener(metaclass=ABCMeta):
     matchers: Sequence[ListenerMatcher]
-    middleware: Sequence[Middleware]  # type: ignore
+    middleware: Sequence[Middleware]
     ack_function: Callable[..., BoltResponse]
     lazy_functions: Sequence[Callable[..., None]]
     auto_acknowledgement: bool
@@ -3379,7 +3395,7 @@ 

Class variables

def next_(): middleware_state["next_called"] = True - resp = m.process(req=req, resp=resp, next=next_) + resp = m.process(req=req, resp=resp, next=next_) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) @@ -3596,8 +3612,8 @@

Class variables

text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response - return self.client.chat_postMessage( - channel=channel or self.channel, + return self.client.chat_postMessage( # type: ignore[union-attr] + channel=channel or self.channel, # type: ignore[arg-type] text=text, blocks=blocks, attachments=attachments, @@ -3619,7 +3635,7 @@

Class variables

message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel - return self.client.chat_postMessage(**message) + return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html b/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html index f1f6d86ef..49e597234 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html @@ -34,7 +34,7 @@

Module slack_bolt.kwargs_injection.async_utilsFunctions

-def build_async_required_kwargs(*, logger: logging.Logger, required_arg_names: Sequence[str], request: AsyncBoltRequest, response: Optional[BoltResponse], next_func: Callable[[], None] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any] +def build_async_required_kwargs(*, logger: logging.Logger, required_arg_names: MutableSequence[str], request: AsyncBoltRequest, response: Optional[BoltResponse], next_func: Optional[Callable[[], None]] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any]
diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/index.html b/docs/static/api-docs/slack_bolt/kwargs_injection/index.html index 934e9289c..f44ca9c83 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/index.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/index.html @@ -56,7 +56,7 @@

Sub-modules

Functions

-def build_required_kwargs(*, logger: logging.Logger, required_arg_names: Sequence[str], request: BoltRequest, response: Optional[BoltResponse], next_func: Callable[[], None] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any] +def build_required_kwargs(*, logger: logging.Logger, required_arg_names: MutableSequence[str], request: BoltRequest, response: Optional[BoltResponse], next_func: Optional[Callable[[], None]] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any]
diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html b/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html index 8ba7c772a..443b6a89f 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html @@ -34,7 +34,7 @@

Module slack_bolt.kwargs_injection.utils

Functions

-def build_required_kwargs(*, logger: logging.Logger, required_arg_names: Sequence[str], request: BoltRequest, response: Optional[BoltResponse], next_func: Callable[[], None] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any] +def build_required_kwargs(*, logger: logging.Logger, required_arg_names: MutableSequence[str], request: BoltRequest, response: Optional[BoltResponse], next_func: Optional[Callable[[], None]] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any]
diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html b/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html index 16b2a68a3..389ae4cfa 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html @@ -69,7 +69,7 @@

Classes

logger=self.logger, request=request, ) - return await func() # type: ignore
+ return await func() # type: ignore[operator]

Subclasses

    diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener.html b/docs/static/api-docs/slack_bolt/listener/async_listener.html index fcb5c2ee8..6b755c213 100644 --- a/docs/static/api-docs/slack_bolt/listener/async_listener.html +++ b/docs/static/api-docs/slack_bolt/listener/async_listener.html @@ -47,12 +47,12 @@

    Classes

    class AsyncCustomListener(AsyncListener):
         app_name: str
    -    ack_function: Callable[..., Awaitable[Optional[BoltResponse]]]
    +    ack_function: Callable[..., Awaitable[Optional[BoltResponse]]]  # type: ignore[assignment]
         lazy_functions: Sequence[Callable[..., Awaitable[None]]]
         matchers: Sequence[AsyncListenerMatcher]
         middleware: Sequence[AsyncMiddleware]
         auto_acknowledgement: bool
    -    arg_names: Sequence[str]
    +    arg_names: MutableSequence[str]
         logger: Logger
     
         def __init__(
    @@ -105,7 +105,7 @@ 

    Class variables

    -
    var arg_names : Sequence[str]
    +
    var arg_names : MutableSequence[str]
    @@ -161,12 +161,12 @@

    Returns

    class AsyncCustomListener(AsyncListener):
         app_name: str
    -    ack_function: Callable[..., Awaitable[Optional[BoltResponse]]]
    +    ack_function: Callable[..., Awaitable[Optional[BoltResponse]]]  # type: ignore[assignment]
         lazy_functions: Sequence[Callable[..., Awaitable[None]]]
         matchers: Sequence[AsyncListenerMatcher]
         middleware: Sequence[AsyncMiddleware]
         auto_acknowledgement: bool
    -    arg_names: Sequence[str]
    +    arg_names: MutableSequence[str]
         logger: Logger
     
         def __init__(
    @@ -219,7 +219,7 @@ 

    Class variables

    -
    var arg_names : Sequence[str]
    +
    var arg_names : MutableSequence[str]
    @@ -304,7 +304,7 @@

    Inherited members

    async def _next(): middleware_state["next_called"] = True - resp = await m.async_process(req=req, resp=resp, next=_next) + resp = await m.async_process(req=req, resp=resp, next=_next) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html b/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html index 440a32248..97a356f5d 100644 --- a/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html @@ -67,9 +67,9 @@

    Classes

    ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status - response.headers = returned_response.headers - response.body = returned_response.body
    + response.status = returned_response.status # type: ignore[union-attr] + response.headers = returned_response.headers # type: ignore[union-attr] + response.body = returned_response.body # type: ignore[union-attr]

    Ancestors

      diff --git a/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html b/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html index b644d7d68..a98c3fe3f 100644 --- a/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html +++ b/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html @@ -144,7 +144,7 @@

      Classes

      response = BoltResponse(status=500) response.status = 500 if ack.response is not None: # already acknowledged - response = None + response = None # type: ignore[assignment] await self.listener_error_handler.handle( error=e, diff --git a/docs/static/api-docs/slack_bolt/listener/custom_listener.html b/docs/static/api-docs/slack_bolt/listener/custom_listener.html index f54b7d0d7..a21bf9470 100644 --- a/docs/static/api-docs/slack_bolt/listener/custom_listener.html +++ b/docs/static/api-docs/slack_bolt/listener/custom_listener.html @@ -47,12 +47,12 @@

      Classes

      class CustomListener(Listener):
           app_name: str
      -    ack_function: Callable[..., Optional[BoltResponse]]
      +    ack_function: Callable[..., Optional[BoltResponse]]  # type: ignore[assignment]
           lazy_functions: Sequence[Callable[..., None]]
           matchers: Sequence[ListenerMatcher]
      -    middleware: Sequence[Middleware]  # type: ignore
      +    middleware: Sequence[Middleware]
           auto_acknowledgement: bool
      -    arg_names: Sequence[str]
      +    arg_names: MutableSequence[str]
           logger: Logger
       
           def __init__(
      @@ -62,7 +62,7 @@ 

      Classes

      ack_function: Callable[..., Optional[BoltResponse]], lazy_functions: Sequence[Callable[..., None]], matchers: Sequence[ListenerMatcher], - middleware: Sequence[Middleware], # type: ignore + middleware: Sequence[Middleware], auto_acknowledgement: bool = False, base_logger: Optional[Logger] = None, ): @@ -105,7 +105,7 @@

      Class variables

      -
      var arg_names : Sequence[str]
      +
      var arg_names : MutableSequence[str]
      diff --git a/docs/static/api-docs/slack_bolt/listener/index.html b/docs/static/api-docs/slack_bolt/listener/index.html index b429975f6..9fe889217 100644 --- a/docs/static/api-docs/slack_bolt/listener/index.html +++ b/docs/static/api-docs/slack_bolt/listener/index.html @@ -106,12 +106,12 @@

      Classes

      class CustomListener(Listener):
           app_name: str
      -    ack_function: Callable[..., Optional[BoltResponse]]
      +    ack_function: Callable[..., Optional[BoltResponse]]  # type: ignore[assignment]
           lazy_functions: Sequence[Callable[..., None]]
           matchers: Sequence[ListenerMatcher]
      -    middleware: Sequence[Middleware]  # type: ignore
      +    middleware: Sequence[Middleware]
           auto_acknowledgement: bool
      -    arg_names: Sequence[str]
      +    arg_names: MutableSequence[str]
           logger: Logger
       
           def __init__(
      @@ -121,7 +121,7 @@ 

      Classes

      ack_function: Callable[..., Optional[BoltResponse]], lazy_functions: Sequence[Callable[..., None]], matchers: Sequence[ListenerMatcher], - middleware: Sequence[Middleware], # type: ignore + middleware: Sequence[Middleware], auto_acknowledgement: bool = False, base_logger: Optional[Logger] = None, ): @@ -164,7 +164,7 @@

      Class variables

      -
      var arg_names : Sequence[str]
      +
      var arg_names : MutableSequence[str]
      @@ -210,7 +210,7 @@

      Inherited members

      class Listener(metaclass=ABCMeta):
           matchers: Sequence[ListenerMatcher]
      -    middleware: Sequence[Middleware]  # type: ignore
      +    middleware: Sequence[Middleware]
           ack_function: Callable[..., BoltResponse]
           lazy_functions: Sequence[Callable[..., None]]
           auto_acknowledgement: bool
      @@ -249,7 +249,7 @@ 

      Inherited members

      def next_(): middleware_state["next_called"] = True - resp = m.process(req=req, resp=resp, next=next_) + resp = m.process(req=req, resp=resp, next=next_) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) diff --git a/docs/static/api-docs/slack_bolt/listener/listener.html b/docs/static/api-docs/slack_bolt/listener/listener.html index c0437c373..d705207a8 100644 --- a/docs/static/api-docs/slack_bolt/listener/listener.html +++ b/docs/static/api-docs/slack_bolt/listener/listener.html @@ -46,7 +46,7 @@

      Classes

      class Listener(metaclass=ABCMeta):
           matchers: Sequence[ListenerMatcher]
      -    middleware: Sequence[Middleware]  # type: ignore
      +    middleware: Sequence[Middleware]
           ack_function: Callable[..., BoltResponse]
           lazy_functions: Sequence[Callable[..., None]]
           auto_acknowledgement: bool
      @@ -85,7 +85,7 @@ 

      Classes

      def next_(): middleware_state["next_called"] = True - resp = m.process(req=req, resp=resp, next=next_) + resp = m.process(req=req, resp=resp, next=next_) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) diff --git a/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html b/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html index a4964b7f2..d7895959b 100644 --- a/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html @@ -67,9 +67,9 @@

      Classes

      ) returned_response = self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status - response.headers = returned_response.headers - response.body = returned_response.body
      + response.status = returned_response.status # type: ignore[union-attr] + response.headers = returned_response.headers # type: ignore[union-attr] + response.body = returned_response.body # type: ignore[union-attr]

      Ancestors

        diff --git a/docs/static/api-docs/slack_bolt/listener/thread_runner.html b/docs/static/api-docs/slack_bolt/listener/thread_runner.html index 4ca30857a..7b9ae9f2b 100644 --- a/docs/static/api-docs/slack_bolt/listener/thread_runner.html +++ b/docs/static/api-docs/slack_bolt/listener/thread_runner.html @@ -72,7 +72,7 @@

        Classes

        self.listener_executor = listener_executor self.lazy_listener_runner = lazy_listener_runner - def run( # type: ignore + def run( self, request: BoltRequest, response: BoltResponse, diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html b/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html index 5e0a31e28..1ef4b5c63 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html @@ -47,7 +47,7 @@

        Classes

        class AsyncBuiltinListenerMatcher(BuiltinListenerMatcher, AsyncListenerMatcher):
             async def async_matches(self, req: AsyncBoltRequest, resp: BoltResponse) -> bool:
        -        return await self.func(
        +        return await self.func(  # type: ignore[misc]
                     **build_async_required_kwargs(
                         logger=self.logger,
                         required_arg_names=self.arg_names,
        diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html b/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html
        index 6c31d3275..eba4cd7ad 100644
        --- a/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html
        +++ b/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html
        @@ -61,7 +61,7 @@ 

        Classes

        return await self.func( **build_async_required_kwargs( logger=self.logger, - required_arg_names=self.arg_names, + required_arg_names=self.arg_names, # type: ignore[arg-type] request=req, response=resp, this_func=self.func, @@ -136,7 +136,7 @@

        Returns

        return await self.func( **build_async_required_kwargs( logger=self.logger, - required_arg_names=self.arg_names, + required_arg_names=self.arg_names, # type: ignore[arg-type] request=req, response=resp, this_func=self.func, diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html b/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html index 0f56fe8ad..1b8f0f019 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html @@ -192,7 +192,7 @@

        Classes

        self.logger = get_bolt_logger(self.func, base_logger) def matches(self, req: BoltRequest, resp: BoltResponse) -> bool: - return self.func( + return self.func( # type: ignore[return-value] **build_required_kwargs( logger=self.logger, required_arg_names=self.arg_names, diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html b/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html index e83533618..c34cd0311 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html @@ -48,7 +48,7 @@

        Classes

        class CustomListenerMatcher(ListenerMatcher):
             app_name: str
             func: Callable[..., bool]
        -    arg_names: Sequence[str]
        +    arg_names: MutableSequence[str]
             logger: Logger
         
             def __init__(self, *, app_name: str, func: Callable[..., bool], base_logger: Optional[Logger] = None):
        @@ -78,7 +78,7 @@ 

        Class variables

        -
        var arg_names : Sequence[str]
        +
        var arg_names : MutableSequence[str]
        diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/index.html b/docs/static/api-docs/slack_bolt/listener_matcher/index.html index f2dfd80d9..292e8836f 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/index.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/index.html @@ -75,7 +75,7 @@

        Classes

        class CustomListenerMatcher(ListenerMatcher):
             app_name: str
             func: Callable[..., bool]
        -    arg_names: Sequence[str]
        +    arg_names: MutableSequence[str]
             logger: Logger
         
             def __init__(self, *, app_name: str, func: Callable[..., bool], base_logger: Optional[Logger] = None):
        @@ -105,7 +105,7 @@ 

        Class variables

        -
        var arg_names : Sequence[str]
        +
        var arg_names : MutableSequence[str]
        diff --git a/docs/static/api-docs/slack_bolt/logger/messages.html b/docs/static/api-docs/slack_bolt/logger/messages.html index 94acd8944..9a6801595 100644 --- a/docs/static/api-docs/slack_bolt/logger/messages.html +++ b/docs/static/api-docs/slack_bolt/logger/messages.html @@ -117,6 +117,12 @@

        Functions

        +
        +def error_oauth_flow_or_authorize_required() ‑> str +
        +
        +
        +
        def error_oauth_settings_invalid_type_async() ‑> str
        @@ -220,6 +226,7 @@

        Functions

      • error_listener_function_must_be_coro_func
      • error_message_event_type
      • error_oauth_flow_invalid_type_async
      • +
      • error_oauth_flow_or_authorize_required
      • error_oauth_settings_invalid_type_async
      • error_token_required
      • error_unexpected_listener_middleware
      • diff --git a/docs/static/api-docs/slack_bolt/middleware/async_builtins.html b/docs/static/api-docs/slack_bolt/middleware/async_builtins.html index b13852d2f..12ac1b808 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_builtins.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_builtins.html @@ -44,7 +44,7 @@

        Classes

        Expand source code -
        class AsyncAttachingFunctionToken(AsyncMiddleware):  # type: ignore
        +
        class AsyncAttachingFunctionToken(AsyncMiddleware):
             async def async_process(
                 self,
                 *,
        @@ -54,7 +54,7 @@ 

        Classes

        next: Callable[[], Awaitable[BoltResponse]], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token + req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] return await next()
        @@ -94,7 +94,7 @@

        Inherited members

        auth_result = req.context.authorize_result # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") - if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): + if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] self._debug_log(req.body) return await req.context.ack() else: @@ -149,7 +149,7 @@

        Inherited members

        ) -> BoltResponse: text = req.body.get("event", {}).get("text", "") if text: - m = re.findall(self.keyword, text) + m: Optional[Union[Sequence]] = re.findall(self.keyword, text) if m is not None and m != []: if type(m[0]) is not tuple: m = tuple(m) diff --git a/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html b/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html index ff4996c6a..132eb6b41 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html @@ -48,7 +48,7 @@

        Classes

        class AsyncCustomMiddleware(AsyncMiddleware):
             app_name: str
             func: Callable[..., Awaitable[Any]]
        -    arg_names: Sequence[str]
        +    arg_names: MutableSequence[str]
             logger: Logger
         
             def __init__(
        @@ -59,7 +59,7 @@ 

        Classes

        base_logger: Optional[Logger] = None, ): self.app_name = app_name - if inspect.iscoroutinefunction(func): + if is_callable_coroutine(func): self.func = func else: raise ValueError("Async middleware function must be an async function") @@ -83,7 +83,7 @@

        Classes

        required_arg_names=self.arg_names, request=req, response=resp, - next_func=next, + next_func=next, # type: ignore[arg-type] this_func=self.func, ) ) @@ -102,7 +102,7 @@

        Class variables

        -
        var arg_names : Sequence[str]
        +
        var arg_names : MutableSequence[str]
        diff --git a/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html b/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html index 8cb28fe7a..f899b7264 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html @@ -67,9 +67,9 @@

        Classes

        ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status - response.headers = returned_response.headers - response.body = returned_response.body
        + response.status = returned_response.status # type: ignore[union-attr] + response.headers = returned_response.headers # type: ignore[union-attr] + response.body = returned_response.body # type: ignore[union-attr]

        Ancestors

          diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html index 7f330bd97..0ac3072f7 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html @@ -44,7 +44,7 @@

          Classes

          Expand source code -
          class AsyncAttachingFunctionToken(AsyncMiddleware):  # type: ignore
          +
          class AsyncAttachingFunctionToken(AsyncMiddleware):
               async def async_process(
                   self,
                   *,
          @@ -54,7 +54,7 @@ 

          Classes

          next: Callable[[], Awaitable[BoltResponse]], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token + req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] return await next()
          diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html index d49a4820f..8f501d9f2 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html @@ -44,7 +44,7 @@

          Classes

          Expand source code -
          class AttachingFunctionToken(Middleware):  # type: ignore
          +
          class AttachingFunctionToken(Middleware):
               def process(
                   self,
                   *,
          @@ -54,7 +54,7 @@ 

          Classes

          next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token + req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] return next()
          diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html index 627bb5124..06ba97ee7 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html @@ -55,7 +55,7 @@

          Classes

          Expand source code -
          class AttachingFunctionToken(Middleware):  # type: ignore
          +
          class AttachingFunctionToken(Middleware):
               def process(
                   self,
                   *,
          @@ -65,7 +65,7 @@ 

          Classes

          next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token + req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] return next()
          diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html index ab94ce363..7ec390f38 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html @@ -131,7 +131,7 @@

          Args

          req.context["token"] = token # As AsyncApp#_init_context() generates a new AsyncWebClient for this request, # it's safe to modify this instance. - req.context.client.token = token + req.context.client.token = token # type: ignore[union-attr] return await next() else: # This situation can arise if: @@ -142,7 +142,7 @@

          Args

          "the AuthorizeResult (returned value from authorize) for it was not found." ) if req.context.response_url is not None: - await req.context.respond(self.user_facing_authorize_error_message) + await req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html index f3e5e5a83..8179b6c29 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html @@ -84,13 +84,13 @@

          Classes

          try: if self.auth_test_result is None: - self.auth_test_result = await req.context.client.auth_test() + self.auth_test_result = await req.context.client.auth_test() # type: ignore[union-attr] if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, + token=req.context.client.token, # type: ignore[union-attr] request_user_id=req.context.user_id, ) ) @@ -99,7 +99,7 @@

          Classes

          # Just in case self.logger.error("auth.test API call result is unexpectedly None") if req.context.response_url is not None: - await req.context.respond(self.user_facing_authorize_error_message) + await req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/index.html b/docs/static/api-docs/slack_bolt/middleware/authorization/index.html index 612e00c8e..d72ad72c9 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/index.html @@ -195,7 +195,7 @@

          Args

          req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. - req.context.client.token = token + req.context.client.token = token # type: ignore[union-attr] return next() else: # This situation can arise if: @@ -206,7 +206,7 @@

          Args

          "the AuthorizeResult (returned value from authorize) for it was not found." ) if req.context.response_url is not None: - req.context.respond(self.user_facing_authorize_error_message) + req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) @@ -303,13 +303,13 @@

          Args

          try: if not self.auth_test_result: - self.auth_test_result = req.context.client.auth_test() + self.auth_test_result = req.context.client.auth_test() # type: ignore[union-attr] if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, + token=req.context.client.token, # type: ignore[union-attr] request_user_id=req.context.user_id, ) ) @@ -318,7 +318,7 @@

          Args

          # Just in case self.logger.error("auth.test API call result is unexpectedly None") if req.context.response_url is not None: - req.context.respond(self.user_facing_authorize_error_message) + req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html index e7a02bb05..f5400a4d4 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html @@ -129,7 +129,7 @@

          Args

          req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. - req.context.client.token = token + req.context.client.token = token # type: ignore[union-attr] return next() else: # This situation can arise if: @@ -140,7 +140,7 @@

          Args

          "the AuthorizeResult (returned value from authorize) for it was not found." ) if req.context.response_url is not None: - req.context.respond(self.user_facing_authorize_error_message) + req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html index 91bd962f7..29dc60414 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html @@ -98,13 +98,13 @@

          Args

          try: if not self.auth_test_result: - self.auth_test_result = req.context.client.auth_test() + self.auth_test_result = req.context.client.auth_test() # type: ignore[union-attr] if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, + token=req.context.client.token, # type: ignore[union-attr] request_user_id=req.context.user_id, ) ) @@ -113,7 +113,7 @@

          Args

          # Just in case self.logger.error("auth.test API call result is unexpectedly None") if req.context.response_url is not None: - req.context.respond(self.user_facing_authorize_error_message) + req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: diff --git a/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html b/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html index e4fab5496..fd8efc789 100644 --- a/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html +++ b/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html @@ -48,7 +48,7 @@

          Classes

          class CustomMiddleware(Middleware):
               app_name: str
               func: Callable[..., Any]
          -    arg_names: Sequence[str]
          +    arg_names: MutableSequence[str]
               logger: Logger
           
               def __init__(self, *, app_name: str, func: Callable, base_logger: Optional[Logger] = None):
          @@ -73,7 +73,7 @@ 

          Classes

          required_arg_names=self.arg_names, request=req, response=resp, - next_func=next, + next_func=next, # type: ignore[arg-type] this_func=self.func, ) ) @@ -92,7 +92,7 @@

          Class variables

          -
          var arg_names : Sequence[str]
          +
          var arg_names : MutableSequence[str]
          diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html index 97b831441..d02a44677 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html @@ -57,7 +57,7 @@

          Classes

          auth_result = req.context.authorize_result # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") - if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): + if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] self._debug_log(req.body) return await req.context.ack() else: diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html index 9d825287f..e5e9222d6 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html @@ -61,7 +61,7 @@

          Classes

          auth_result = req.context.authorize_result # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") - if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): + if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] self._debug_log(req.body) return req.context.ack() else: diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html index 63a33ceb4..68eef6bfd 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html @@ -72,7 +72,7 @@

          Classes

          auth_result = req.context.authorize_result # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") - if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): + if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] self._debug_log(req.body) return req.context.ack() else: diff --git a/docs/static/api-docs/slack_bolt/middleware/index.html b/docs/static/api-docs/slack_bolt/middleware/index.html index 58ae3d02d..59cf0b3fc 100644 --- a/docs/static/api-docs/slack_bolt/middleware/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/index.html @@ -108,7 +108,7 @@

          Classes

          Expand source code -
          class AttachingFunctionToken(Middleware):  # type: ignore
          +
          class AttachingFunctionToken(Middleware):
               def process(
                   self,
                   *,
          @@ -118,7 +118,7 @@ 

          Classes

          next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token + req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] return next()
          @@ -149,7 +149,7 @@

          Inherited members

          class CustomMiddleware(Middleware):
               app_name: str
               func: Callable[..., Any]
          -    arg_names: Sequence[str]
          +    arg_names: MutableSequence[str]
               logger: Logger
           
               def __init__(self, *, app_name: str, func: Callable, base_logger: Optional[Logger] = None):
          @@ -174,7 +174,7 @@ 

          Inherited members

          required_arg_names=self.arg_names, request=req, response=resp, - next_func=next, + next_func=next, # type: ignore[arg-type] this_func=self.func, ) ) @@ -193,7 +193,7 @@

          Class variables

          -
          var arg_names : Sequence[str]
          +
          var arg_names : MutableSequence[str]
          @@ -242,7 +242,7 @@

          Inherited members

          auth_result = req.context.authorize_result # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") - if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): + if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] self._debug_log(req.body) return req.context.ack() else: @@ -513,7 +513,7 @@

          Args

          req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. - req.context.client.token = token + req.context.client.token = token # type: ignore[union-attr] return next() else: # This situation can arise if: @@ -524,7 +524,7 @@

          Args

          "the AuthorizeResult (returned value from authorize) for it was not found." ) if req.context.response_url is not None: - req.context.respond(self.user_facing_authorize_error_message) + req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) @@ -578,7 +578,7 @@

          Args

          Expand source code -
          class RequestVerification(Middleware):  # type: ignore
          +
          class RequestVerification(Middleware):
               def __init__(self, signing_secret: str, base_logger: Optional[Logger] = None):
                   """Verifies an incoming request by checking the validity of
                   `x-slack-signature`, `x-slack-request-timestamp`, and its body data.
          @@ -710,13 +710,13 @@ 

          Args

          try: if not self.auth_test_result: - self.auth_test_result = req.context.client.auth_test() + self.auth_test_result = req.context.client.auth_test() # type: ignore[union-attr] if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, + token=req.context.client.token, # type: ignore[union-attr] request_user_id=req.context.user_id, ) ) @@ -725,7 +725,7 @@

          Args

          # Just in case self.logger.error("auth.test API call result is unexpectedly None") if req.context.response_url is not None: - req.context.respond(self.user_facing_authorize_error_message) + req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: @@ -767,7 +767,7 @@

          Args

          Expand source code -
          class SslCheck(Middleware):  # type: ignore
          +
          class SslCheck(Middleware):
               verification_token: Optional[str]
               logger: Logger
           
          @@ -867,7 +867,7 @@ 

          Args

          Expand source code -
          class UrlVerification(Middleware):  # type: ignore
          +
          class UrlVerification(Middleware):
               def __init__(self, base_logger: Optional[Logger] = None):
                   """Handles url_verification requests.
           
          diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html
          index f2f3ca0d1..081535c83 100644
          --- a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html
          +++ b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html
          @@ -63,7 +63,7 @@ 

          Classes

          ) -> BoltResponse: text = req.body.get("event", {}).get("text", "") if text: - m = re.findall(self.keyword, text) + m: Optional[Union[Sequence]] = re.findall(self.keyword, text) if m is not None and m != []: if type(m[0]) is not tuple: m = tuple(m) diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html index 63c6fb8c6..7a49942c4 100644 --- a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html @@ -57,7 +57,7 @@

          Classes

          Expand source code -
          class MessageListenerMatches(Middleware):  # type: ignore
          +
          class MessageListenerMatches(Middleware):
               def __init__(self, keyword: Union[str, Pattern]):
                   """Captures matched keywords and saves the values in context."""
                   self.keyword = keyword
          @@ -74,7 +74,7 @@ 

          Classes

          ) -> BoltResponse: text = req.body.get("event", {}).get("text", "") if text: - m = re.findall(self.keyword, text) + m: Optional[Union[Sequence]] = re.findall(self.keyword, text) if m is not None and m != []: if type(m[0]) is not tuple: m = tuple(m) diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html index 151d080e4..6e7716218 100644 --- a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html +++ b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html @@ -46,7 +46,7 @@

          Classes

          Expand source code -
          class MessageListenerMatches(Middleware):  # type: ignore
          +
          class MessageListenerMatches(Middleware):
               def __init__(self, keyword: Union[str, Pattern]):
                   """Captures matched keywords and saves the values in context."""
                   self.keyword = keyword
          @@ -63,7 +63,7 @@ 

          Classes

          ) -> BoltResponse: text = req.body.get("event", {}).get("text", "") if text: - m = re.findall(self.keyword, text) + m: Optional[Union[Sequence]] = re.findall(self.keyword, text) if m is not None and m != []: if type(m[0]) is not tuple: m = tuple(m) diff --git a/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html b/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html index ad7ca0dc8..6794d4927 100644 --- a/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html +++ b/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html @@ -67,9 +67,9 @@

          Classes

          ) returned_response = self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status - response.headers = returned_response.headers - response.body = returned_response.body
          + response.status = returned_response.status # type: ignore[union-attr] + response.headers = returned_response.headers # type: ignore[union-attr] + response.body = returned_response.body # type: ignore[union-attr]

          Ancestors

            @@ -135,7 +135,7 @@

            Inherited members

            self, error: Exception, request: BoltRequest, - response: Optional[BoltResponse], + response: Optional[BoltResponse], # TODO: why is this optional ) -> None: """Handles an unhandled exception. diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html b/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html index ae5601445..bc08f3c88 100644 --- a/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html @@ -66,7 +66,7 @@

            Args

            Expand source code -
            class RequestVerification(Middleware):  # type: ignore
            +
            class RequestVerification(Middleware):
                 def __init__(self, signing_secret: str, base_logger: Optional[Logger] = None):
                     """Verifies an incoming request by checking the validity of
                     `x-slack-signature`, `x-slack-request-timestamp`, and its body data.
            diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html b/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html
            index 3551da02a..46e6c9f1b 100644
            --- a/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html
            +++ b/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html
            @@ -55,7 +55,7 @@ 

            Args

            Expand source code -
            class RequestVerification(Middleware):  # type: ignore
            +
            class RequestVerification(Middleware):
                 def __init__(self, signing_secret: str, base_logger: Optional[Logger] = None):
                     """Verifies an incoming request by checking the validity of
                     `x-slack-signature`, `x-slack-request-timestamp`, and its body data.
            diff --git a/docs/static/api-docs/slack_bolt/middleware/ssl_check/index.html b/docs/static/api-docs/slack_bolt/middleware/ssl_check/index.html
            index 52af390b7..d86616a1c 100644
            --- a/docs/static/api-docs/slack_bolt/middleware/ssl_check/index.html
            +++ b/docs/static/api-docs/slack_bolt/middleware/ssl_check/index.html
            @@ -66,7 +66,7 @@ 

            Args

            Expand source code -
            class SslCheck(Middleware):  # type: ignore
            +
            class SslCheck(Middleware):
                 verification_token: Optional[str]
                 logger: Logger
             
            diff --git a/docs/static/api-docs/slack_bolt/middleware/ssl_check/ssl_check.html b/docs/static/api-docs/slack_bolt/middleware/ssl_check/ssl_check.html
            index d111dc000..4da70ebc5 100644
            --- a/docs/static/api-docs/slack_bolt/middleware/ssl_check/ssl_check.html
            +++ b/docs/static/api-docs/slack_bolt/middleware/ssl_check/ssl_check.html
            @@ -55,7 +55,7 @@ 

            Args

            Expand source code -
            class SslCheck(Middleware):  # type: ignore
            +
            class SslCheck(Middleware):
                 verification_token: Optional[str]
                 logger: Logger
             
            diff --git a/docs/static/api-docs/slack_bolt/middleware/url_verification/index.html b/docs/static/api-docs/slack_bolt/middleware/url_verification/index.html
            index b440bc0d6..e0f1ec73f 100644
            --- a/docs/static/api-docs/slack_bolt/middleware/url_verification/index.html
            +++ b/docs/static/api-docs/slack_bolt/middleware/url_verification/index.html
            @@ -63,7 +63,7 @@ 

            Args

            Expand source code -
            class UrlVerification(Middleware):  # type: ignore
            +
            class UrlVerification(Middleware):
                 def __init__(self, base_logger: Optional[Logger] = None):
                     """Handles url_verification requests.
             
            diff --git a/docs/static/api-docs/slack_bolt/middleware/url_verification/url_verification.html b/docs/static/api-docs/slack_bolt/middleware/url_verification/url_verification.html
            index ce93de733..10200fd34 100644
            --- a/docs/static/api-docs/slack_bolt/middleware/url_verification/url_verification.html
            +++ b/docs/static/api-docs/slack_bolt/middleware/url_verification/url_verification.html
            @@ -52,7 +52,7 @@ 

            Args

            Expand source code -
            class UrlVerification(Middleware):  # type: ignore
            +
            class UrlVerification(Middleware):
                 def __init__(self, base_logger: Optional[Logger] = None):
                     """Handles url_verification requests.
             
            diff --git a/docs/static/api-docs/slack_bolt/oauth/async_callback_options.html b/docs/static/api-docs/slack_bolt/oauth/async_callback_options.html
            index 2d344104e..51b4e4b5f 100644
            --- a/docs/static/api-docs/slack_bolt/oauth/async_callback_options.html
            +++ b/docs/static/api-docs/slack_bolt/oauth/async_callback_options.html
            @@ -99,14 +99,14 @@ 

            Args

            Expand source code
            class AsyncFailureArgs:
            -    def __init__(  # type: ignore
            +    def __init__(
                     self,
                     *,
                     request: AsyncBoltRequest,
                     reason: str,
                     error: Optional[Exception] = None,
                     suggested_status_code: int,
            -        settings: "AsyncOAuthSettings",
            +        settings: "AsyncOAuthSettings",  # type: ignore[name-defined]
                     default: "AsyncCallbackOptions",
                 ):
                     """The arguments for a failure function.
            @@ -149,12 +149,12 @@ 

            Args

            Expand source code
            class AsyncSuccessArgs:
            -    def __init__(  # type: ignore
            +    def __init__(
                     self,
                     *,
                     request: AsyncBoltRequest,
                     installation: Installation,
            -        settings: "AsyncOAuthSettings",
            +        settings: "AsyncOAuthSettings",  # type: ignore[name-defined]
                     default: "AsyncCallbackOptions",
                 ):
                     """The arguments for a success function.
            @@ -197,10 +197,8 @@ 

            Args

            state_utils=state_utils, redirect_uri_page_renderer=redirect_uri_page_renderer, ) - # Note that pytype 2021.4.26 misunderstands these assignments. - # Thus, we put "type: ignore" for the following two lines - self.success = self._success_handler # type: ignore - self.failure = self._failure_handler # type: ignore + self.success = self._success_handler + self.failure = self._failure_handler # -------------------------- # Internal methods diff --git a/docs/static/api-docs/slack_bolt/oauth/async_oauth_flow.html b/docs/static/api-docs/slack_bolt/oauth/async_oauth_flow.html index 4613a858a..04e2dcf45 100644 --- a/docs/static/api-docs/slack_bolt/oauth/async_oauth_flow.html +++ b/docs/static/api-docs/slack_bolt/oauth/async_oauth_flow.html @@ -64,18 +64,6 @@

            Args

            success_handler: Callable[[AsyncSuccessArgs], Awaitable[BoltResponse]] failure_handler: Callable[[AsyncFailureArgs], Awaitable[BoltResponse]] - @property - def client(self) -> AsyncWebClient: - if self._async_client is None: - self._async_client = create_async_web_client(logger=self.logger) - return self._async_client - - @property - def logger(self) -> Logger: - if self._logger is None: - self._logger = logging.getLogger(__name__) - return self._logger - def __init__( self, *, @@ -97,7 +85,8 @@

            Args

            raise BoltError(error_oauth_settings_invalid_type_async()) self.settings = settings - self.settings.logger = self._logger + if self._logger is not None: + self.settings.logger = self._logger self.client_id = self.settings.client_id self.redirect_uri = self.settings.redirect_uri @@ -105,7 +94,7 @@

            Args

            self.redirect_uri_path = self.settings.redirect_uri_path self.default_callback_options = DefaultAsyncCallbackOptions( - logger=logger, + logger=logger, # type: ignore[arg-type] state_utils=self.settings.state_utils, redirect_uri_page_renderer=self.settings.redirect_uri_page_renderer, ) @@ -114,6 +103,18 @@

            Args

            self.success_handler = settings.callback_options.success self.failure_handler = settings.callback_options.failure + @property + def client(self) -> AsyncWebClient: + if self._async_client is None: + self._async_client = create_async_web_client(logger=self.logger) + return self._async_client + + @property + def logger(self) -> Logger: + if self._logger is None: + self._logger = logging.getLogger(__name__) + return self._logger + # ----------------------------- # Factory Methods # ----------------------------- @@ -149,6 +150,16 @@

            Args

            scopes = scopes or os.environ.get("SLACK_SCOPES", "").split(",") user_scopes = user_scopes or os.environ.get("SLACK_USER_SCOPES", "").split(",") redirect_uri = redirect_uri or os.environ.get("SLACK_REDIRECT_URI") + installation_store = ( + SQLite3InstallationStore(database=database, client_id=client_id) + if logger is None + else SQLite3InstallationStore(database=database, client_id=client_id, logger=logger) + ) + state_store = ( + SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds) + if logger is None + else SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds, logger=logger) + ) return AsyncOAuthFlow( client=client or AsyncWebClient(), logger=logger, @@ -161,24 +172,16 @@

            Args

            user_scopes=user_scopes, redirect_uri=redirect_uri, # Handler configuration - install_path=install_path, - redirect_uri_path=redirect_uri_path, + install_path=install_path, # type: ignore[arg-type] + redirect_uri_path=redirect_uri_path, # type: ignore[arg-type] callback_options=callback_options, success_url=success_url, failure_url=failure_url, # Installation Management - installation_store=SQLite3InstallationStore( - database=database, - client_id=client_id, - logger=logger, - ), + installation_store=installation_store, installation_store_bot_only=installation_store_bot_only, # state parameter related configurations - state_store=SQLite3OAuthStateStore( - database=database, - expiration_seconds=state_expiration_seconds, - logger=logger, - ), + state_store=state_store, state_cookie_name=state_cookie_name, state_expiration_seconds=state_expiration_seconds, ), @@ -248,7 +251,7 @@

            Args

            return await self.failure_handler( AsyncFailureArgs( request=request, - reason=error, # type: ignore + reason=error, suggested_status_code=200, settings=self.settings, default=self.default_callback_options, @@ -269,7 +272,7 @@

            Args

            ) ) - valid_state_consumed = await self.settings.state_store.async_consume(state) + valid_state_consumed = await self.settings.state_store.async_consume(state) # type: ignore[arg-type] if not valid_state_consumed: return await self.failure_handler( AsyncFailureArgs( @@ -369,14 +372,14 @@

            Args

            bot_token=bot_token, bot_id=bot_id, bot_user_id=oauth_response.get("bot_user_id"), - bot_scopes=oauth_response.get("scope"), # comma-separated string + bot_scopes=oauth_response.get("scope"), # type: ignore[arg-type] # comma-separated string bot_refresh_token=oauth_response.get("refresh_token"), # since v1.7 bot_token_expires_in=oauth_response.get("expires_in"), # since v1.7 - user_id=installer.get("id"), + user_id=installer.get("id"), # type: ignore[arg-type] user_token=installer.get("access_token"), - user_scopes=installer.get("scope"), # comma-separated string + user_scopes=installer.get("scope"), # type: ignore[arg-type]# comma-separated string user_refresh_token=installer.get("refresh_token"), # since v1.7 - user_token_expires_in=installer.get("expires_in"), # since v1.7 + user_token_expires_in=installer.get("expires_in"), # type: ignore[arg-type] # since v1.7 incoming_webhook_url=incoming_webhook.get("url"), incoming_webhook_channel=incoming_webhook.get("channel"), incoming_webhook_channel_id=incoming_webhook.get("channel_id"), diff --git a/docs/static/api-docs/slack_bolt/oauth/async_oauth_settings.html b/docs/static/api-docs/slack_bolt/oauth/async_oauth_settings.html index b45eeef6c..7ba15525c 100644 --- a/docs/static/api-docs/slack_bolt/oauth/async_oauth_settings.html +++ b/docs/static/api-docs/slack_bolt/oauth/async_oauth_settings.html @@ -187,26 +187,17 @@

            Args

            logger: The logger that will be used internally """ # OAuth flow parameters/credentials - client_id: Optional[str] = client_id or os.environ.get("SLACK_CLIENT_ID") - client_secret: Optional[str] = client_secret or os.environ.get("SLACK_CLIENT_SECRET") + client_id = client_id or os.environ.get("SLACK_CLIENT_ID") + client_secret = client_secret or os.environ.get("SLACK_CLIENT_SECRET") if client_id is None or client_secret is None: raise BoltError("Both client_id and client_secret are required") self.client_id = client_id self.client_secret = client_secret - # NOTE: pytype says that self.scopes can be str, not Sequence[str]. - # That's true but we will check the pattern in the following if statement. - # Thus, we ignore the warnings here. This is the same for user_scopes too. - self.scopes = ( # type: ignore - scopes # type: ignore - if scopes is not None - else os.environ.get("SLACK_SCOPES", "").split(",") # type: ignore - ) # type: ignore + self.scopes = scopes if scopes is not None else os.environ.get("SLACK_SCOPES", "").split(",") if isinstance(self.scopes, str): self.scopes = self.scopes.split(",") - self.user_scopes = ( # type: ignore - user_scopes if user_scopes is not None else os.environ.get("SLACK_USER_SCOPES", "").split(",") # type: ignore - ) # type: ignore + self.user_scopes = user_scopes if user_scopes is not None else os.environ.get("SLACK_USER_SCOPES", "").split(",") if isinstance(self.user_scopes, str): self.user_scopes = self.user_scopes.split(",") diff --git a/docs/static/api-docs/slack_bolt/oauth/callback_options.html b/docs/static/api-docs/slack_bolt/oauth/callback_options.html index 3da4f3596..f7600f463 100644 --- a/docs/static/api-docs/slack_bolt/oauth/callback_options.html +++ b/docs/static/api-docs/slack_bolt/oauth/callback_options.html @@ -181,14 +181,14 @@

            Args

            Expand source code
            class FailureArgs:
            -    def __init__(  # type: ignore
            +    def __init__(
                     self,
                     *,
                     request: BoltRequest,
                     reason: str,
                     error: Optional[Exception] = None,
                     suggested_status_code: int,
            -        settings: "OAuthSettings",
            +        settings: "OAuthSettings",  # type: ignore[name-defined]
                     default: "CallbackOptions",
                 ):
                     """The arguments for a failure function.
            @@ -231,12 +231,12 @@ 

            Args

            Expand source code
            class SuccessArgs:
            -    def __init__(  # type: ignore
            +    def __init__(
                     self,
                     *,
                     request: BoltRequest,
                     installation: Installation,
            -        settings: "OAuthSettings",
            +        settings: "OAuthSettings",  # type: ignore[name-defined]
                     default: "CallbackOptions",
                 ):
                     """The arguments for a success function.
            diff --git a/docs/static/api-docs/slack_bolt/oauth/index.html b/docs/static/api-docs/slack_bolt/oauth/index.html
            index 651d1938e..b217427d4 100644
            --- a/docs/static/api-docs/slack_bolt/oauth/index.html
            +++ b/docs/static/api-docs/slack_bolt/oauth/index.html
            @@ -101,18 +101,6 @@ 

            Args

            success_handler: Callable[[SuccessArgs], BoltResponse] failure_handler: Callable[[FailureArgs], BoltResponse] - @property - def client(self) -> WebClient: - if self._client is None: - self._client = create_web_client(logger=self.logger) - return self._client - - @property - def logger(self) -> Logger: - if self._logger is None: - self._logger = logging.getLogger(__name__) - return self._logger - def __init__( self, *, @@ -130,7 +118,8 @@

            Args

            self._client = client self._logger = logger self.settings = settings - self.settings.logger = self._logger + if self._logger is not None: + self.settings.logger = self._logger self.client_id = self.settings.client_id self.redirect_uri = self.settings.redirect_uri @@ -138,7 +127,7 @@

            Args

            self.redirect_uri_path = self.settings.redirect_uri_path self.default_callback_options = DefaultCallbackOptions( - logger=logger, + logger=logger, # type: ignore[arg-type] state_utils=self.settings.state_utils, redirect_uri_page_renderer=self.settings.redirect_uri_page_renderer, ) @@ -147,6 +136,18 @@

            Args

            self.success_handler = settings.callback_options.success self.failure_handler = settings.callback_options.failure + @property + def client(self) -> WebClient: + if self._client is None: + self._client = create_web_client(logger=self.logger) + return self._client + + @property + def logger(self) -> Logger: + if self._logger is None: + self._logger = logging.getLogger(__name__) + return self._logger + # ----------------------------- # Factory Methods # ----------------------------- @@ -183,6 +184,16 @@

            Args

            scopes = scopes or os.environ.get("SLACK_SCOPES", "").split(",") user_scopes = user_scopes or os.environ.get("SLACK_USER_SCOPES", "").split(",") redirect_uri = redirect_uri or os.environ.get("SLACK_REDIRECT_URI") + installation_store = ( + SQLite3InstallationStore(database=database, client_id=client_id) + if logger is None + else SQLite3InstallationStore(database=database, client_id=client_id, logger=logger) + ) + state_store = ( + SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds) + if logger is None + else SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds, logger=logger) + ) return OAuthFlow( client=client or WebClient(), logger=logger, @@ -194,26 +205,18 @@

            Args

            user_scopes=user_scopes, redirect_uri=redirect_uri, # Handler configuration - install_path=install_path, - redirect_uri_path=redirect_uri_path, + install_path=install_path, # type: ignore[arg-type] + redirect_uri_path=redirect_uri_path, # type: ignore[arg-type] callback_options=callback_options, success_url=success_url, failure_url=failure_url, authorization_url=authorization_url, # Installation Management - installation_store=SQLite3InstallationStore( - database=database, - client_id=client_id, - logger=logger, - ), + installation_store=installation_store, installation_store_bot_only=installation_store_bot_only, token_rotation_expiration_minutes=token_rotation_expiration_minutes, # state parameter related configurations - state_store=SQLite3OAuthStateStore( - database=database, - expiration_seconds=state_expiration_seconds, - logger=logger, - ), + state_store=state_store, state_cookie_name=state_cookie_name, state_expiration_seconds=state_expiration_seconds, ), @@ -305,7 +308,7 @@

            Args

            ) ) - valid_state_consumed = self.settings.state_store.consume(state) + valid_state_consumed = self.settings.state_store.consume(state) # type: ignore[arg-type] if not valid_state_consumed: return self.failure_handler( FailureArgs( @@ -405,14 +408,14 @@

            Args

            bot_token=bot_token, bot_id=bot_id, bot_user_id=oauth_response.get("bot_user_id"), - bot_scopes=oauth_response.get("scope"), # comma-separated string + bot_scopes=oauth_response.get("scope"), # type: ignore[arg-type] # comma-separated string bot_refresh_token=oauth_response.get("refresh_token"), # since v1.7 bot_token_expires_in=oauth_response.get("expires_in"), # since v1.7 - user_id=installer.get("id"), + user_id=installer.get("id"), # type: ignore[arg-type] user_token=installer.get("access_token"), - user_scopes=installer.get("scope"), # comma-separated string + user_scopes=installer.get("scope"), # type: ignore[arg-type] # comma-separated string user_refresh_token=installer.get("refresh_token"), # since v1.7 - user_token_expires_in=installer.get("expires_in"), # since v1.7 + user_token_expires_in=installer.get("expires_in"), # type: ignore[arg-type] # since v1.7 incoming_webhook_url=incoming_webhook.get("url"), incoming_webhook_channel=incoming_webhook.get("channel"), incoming_webhook_channel_id=incoming_webhook.get("channel_id"), diff --git a/docs/static/api-docs/slack_bolt/oauth/internals.html b/docs/static/api-docs/slack_bolt/oauth/internals.html index c87e2eb18..396722b58 100644 --- a/docs/static/api-docs/slack_bolt/oauth/internals.html +++ b/docs/static/api-docs/slack_bolt/oauth/internals.html @@ -78,16 +78,16 @@

            Classes

            self._state_utils = state_utils self._redirect_uri_page_renderer = redirect_uri_page_renderer - def _build_callback_success_response( # type: ignore + def _build_callback_success_response( self, - request: Union[BoltRequest, "AsyncBoltRequest"], + request: Union[BoltRequest, "AsyncBoltRequest"], # type: ignore[name-defined] installation: Installation, ) -> BoltResponse: debug_message = f"Handling an OAuth callback success (request: {request.query})" self._logger.debug(debug_message) page_content = self._redirect_uri_page_renderer.render_success_page( - app_id=installation.app_id, + app_id=installation.app_id, # type: ignore[arg-type] team_id=installation.team_id, is_enterprise_install=installation.is_enterprise_install, enterprise_url=installation.enterprise_url, @@ -101,9 +101,9 @@

            Classes

            body=page_content, ) - def _build_callback_failure_response( # type: ignore + def _build_callback_failure_response( self, - request: Union[BoltRequest, "AsyncBoltRequest"], + request: Union[BoltRequest, "AsyncBoltRequest"], # type: ignore[name-defined] reason: str, status: int = 500, error: Optional[Exception] = None, diff --git a/docs/static/api-docs/slack_bolt/oauth/oauth_flow.html b/docs/static/api-docs/slack_bolt/oauth/oauth_flow.html index 147dcfd5b..47540dcc5 100644 --- a/docs/static/api-docs/slack_bolt/oauth/oauth_flow.html +++ b/docs/static/api-docs/slack_bolt/oauth/oauth_flow.html @@ -64,18 +64,6 @@

            Args

            success_handler: Callable[[SuccessArgs], BoltResponse] failure_handler: Callable[[FailureArgs], BoltResponse] - @property - def client(self) -> WebClient: - if self._client is None: - self._client = create_web_client(logger=self.logger) - return self._client - - @property - def logger(self) -> Logger: - if self._logger is None: - self._logger = logging.getLogger(__name__) - return self._logger - def __init__( self, *, @@ -93,7 +81,8 @@

            Args

            self._client = client self._logger = logger self.settings = settings - self.settings.logger = self._logger + if self._logger is not None: + self.settings.logger = self._logger self.client_id = self.settings.client_id self.redirect_uri = self.settings.redirect_uri @@ -101,7 +90,7 @@

            Args

            self.redirect_uri_path = self.settings.redirect_uri_path self.default_callback_options = DefaultCallbackOptions( - logger=logger, + logger=logger, # type: ignore[arg-type] state_utils=self.settings.state_utils, redirect_uri_page_renderer=self.settings.redirect_uri_page_renderer, ) @@ -110,6 +99,18 @@

            Args

            self.success_handler = settings.callback_options.success self.failure_handler = settings.callback_options.failure + @property + def client(self) -> WebClient: + if self._client is None: + self._client = create_web_client(logger=self.logger) + return self._client + + @property + def logger(self) -> Logger: + if self._logger is None: + self._logger = logging.getLogger(__name__) + return self._logger + # ----------------------------- # Factory Methods # ----------------------------- @@ -146,6 +147,16 @@

            Args

            scopes = scopes or os.environ.get("SLACK_SCOPES", "").split(",") user_scopes = user_scopes or os.environ.get("SLACK_USER_SCOPES", "").split(",") redirect_uri = redirect_uri or os.environ.get("SLACK_REDIRECT_URI") + installation_store = ( + SQLite3InstallationStore(database=database, client_id=client_id) + if logger is None + else SQLite3InstallationStore(database=database, client_id=client_id, logger=logger) + ) + state_store = ( + SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds) + if logger is None + else SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds, logger=logger) + ) return OAuthFlow( client=client or WebClient(), logger=logger, @@ -157,26 +168,18 @@

            Args

            user_scopes=user_scopes, redirect_uri=redirect_uri, # Handler configuration - install_path=install_path, - redirect_uri_path=redirect_uri_path, + install_path=install_path, # type: ignore[arg-type] + redirect_uri_path=redirect_uri_path, # type: ignore[arg-type] callback_options=callback_options, success_url=success_url, failure_url=failure_url, authorization_url=authorization_url, # Installation Management - installation_store=SQLite3InstallationStore( - database=database, - client_id=client_id, - logger=logger, - ), + installation_store=installation_store, installation_store_bot_only=installation_store_bot_only, token_rotation_expiration_minutes=token_rotation_expiration_minutes, # state parameter related configurations - state_store=SQLite3OAuthStateStore( - database=database, - expiration_seconds=state_expiration_seconds, - logger=logger, - ), + state_store=state_store, state_cookie_name=state_cookie_name, state_expiration_seconds=state_expiration_seconds, ), @@ -268,7 +271,7 @@

            Args

            ) ) - valid_state_consumed = self.settings.state_store.consume(state) + valid_state_consumed = self.settings.state_store.consume(state) # type: ignore[arg-type] if not valid_state_consumed: return self.failure_handler( FailureArgs( @@ -368,14 +371,14 @@

            Args

            bot_token=bot_token, bot_id=bot_id, bot_user_id=oauth_response.get("bot_user_id"), - bot_scopes=oauth_response.get("scope"), # comma-separated string + bot_scopes=oauth_response.get("scope"), # type: ignore[arg-type] # comma-separated string bot_refresh_token=oauth_response.get("refresh_token"), # since v1.7 bot_token_expires_in=oauth_response.get("expires_in"), # since v1.7 - user_id=installer.get("id"), + user_id=installer.get("id"), # type: ignore[arg-type] user_token=installer.get("access_token"), - user_scopes=installer.get("scope"), # comma-separated string + user_scopes=installer.get("scope"), # type: ignore[arg-type] # comma-separated string user_refresh_token=installer.get("refresh_token"), # since v1.7 - user_token_expires_in=installer.get("expires_in"), # since v1.7 + user_token_expires_in=installer.get("expires_in"), # type: ignore[arg-type] # since v1.7 incoming_webhook_url=incoming_webhook.get("url"), incoming_webhook_channel=incoming_webhook.get("channel"), incoming_webhook_channel_id=incoming_webhook.get("channel_id"), diff --git a/docs/static/api-docs/slack_bolt/oauth/oauth_settings.html b/docs/static/api-docs/slack_bolt/oauth/oauth_settings.html index 9fa4594c4..239c6822c 100644 --- a/docs/static/api-docs/slack_bolt/oauth/oauth_settings.html +++ b/docs/static/api-docs/slack_bolt/oauth/oauth_settings.html @@ -186,26 +186,17 @@

            Args

            state_expiration_seconds: The seconds that the state value is alive (Default: 600 seconds) logger: The logger that will be used internally """ - client_id: Optional[str] = client_id or os.environ.get("SLACK_CLIENT_ID") - client_secret: Optional[str] = client_secret or os.environ.get("SLACK_CLIENT_SECRET") + client_id = client_id or os.environ.get("SLACK_CLIENT_ID") + client_secret = client_secret or os.environ.get("SLACK_CLIENT_SECRET") if client_id is None or client_secret is None: raise BoltError("Both client_id and client_secret are required") self.client_id = client_id self.client_secret = client_secret - # NOTE: pytype says that self.scopes can be str, not Sequence[str]. - # That's true but we will check the pattern in the following if statement. - # Thus, we ignore the warnings here. This is the same for user_scopes too. - self.scopes = ( # type: ignore - scopes # type: ignore - if scopes is not None - else os.environ.get("SLACK_SCOPES", "").split(",") # type: ignore - ) # type: ignore + self.scopes = scopes if scopes is not None else os.environ.get("SLACK_SCOPES", "").split(",") if isinstance(self.scopes, str): self.scopes = self.scopes.split(",") - self.user_scopes = ( # type: ignore - user_scopes if user_scopes is not None else os.environ.get("SLACK_USER_SCOPES", "").split(",") # type: ignore - ) # type: ignore + self.user_scopes = user_scopes if user_scopes is not None else os.environ.get("SLACK_USER_SCOPES", "").split(",") if isinstance(self.user_scopes, str): self.user_scopes = self.user_scopes.split(",") self.redirect_uri = redirect_uri or os.environ.get("SLACK_REDIRECT_URI") diff --git a/docs/static/api-docs/slack_bolt/util/utils.html b/docs/static/api-docs/slack_bolt/util/utils.html index 78edddbc8..89fd48116 100644 --- a/docs/static/api-docs/slack_bolt/util/utils.html +++ b/docs/static/api-docs/slack_bolt/util/utils.html @@ -82,6 +82,12 @@

            Args

            Returns

            The name of the given Callable object

            +
            +def is_callable_coroutine(func: Optional[Any]) ‑> bool +
            +
            +
            +
@@ -106,6 +112,7 @@

Returns

  • get_arg_names_of_callable
  • get_boot_message
  • get_name_for_callable
  • +
  • is_callable_coroutine
  • diff --git a/docs/static/api-docs/slack_bolt/workflows/step/async_step.html b/docs/static/api-docs/slack_bolt/workflows/step/async_step.html index 090cbddb3..0969b2461 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/async_step.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/async_step.html @@ -556,7 +556,7 @@

    Args

    _matchers.append(AsyncCustomListenerMatcher(app_name=app_name, func=m)) else: raise ValueError(f"Invalid matcher: {type(m)}") - return _matchers # type: ignore + return _matchers @staticmethod def to_listener_middleware( @@ -571,7 +571,7 @@

    Args

    _middleware.append(AsyncCustomMiddleware(app_name=app_name, func=m)) else: raise ValueError(f"Invalid middleware: {type(m)}") - return _middleware # type: ignore
    + return _middleware

    Class variables

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html b/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html index d5753e794..2b324a7b6 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html @@ -45,7 +45,7 @@

    Classes

    Expand source code -
    class AsyncWorkflowStepMiddleware(AsyncMiddleware):  # type:ignore
    +
    class AsyncWorkflowStepMiddleware(AsyncMiddleware):
         """Base middleware for step from app specific ones"""
     
         def __init__(self, step: AsyncWorkflowStep, listener_runner: AsyncioListenerRunner):
    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/index.html b/docs/static/api-docs/slack_bolt/workflows/step/index.html
    index 1bde24cdc..b184a9a2e 100644
    --- a/docs/static/api-docs/slack_bolt/workflows/step/index.html
    +++ b/docs/static/api-docs/slack_bolt/workflows/step/index.html
    @@ -601,7 +601,7 @@ 

    Static methods

    Expand source code -
    class WorkflowStepMiddleware(Middleware):  # type:ignore
    +
    class WorkflowStepMiddleware(Middleware):
         """Base middleware for step from app specific ones"""
     
         def __init__(self, step: WorkflowStep, listener_runner: ThreadListenerRunner):
    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/step.html b/docs/static/api-docs/slack_bolt/workflows/step/step.html
    index 90d742b3d..415fb4612 100644
    --- a/docs/static/api-docs/slack_bolt/workflows/step/step.html
    +++ b/docs/static/api-docs/slack_bolt/workflows/step/step.html
    @@ -577,7 +577,7 @@ 

    Args

    ) else: raise ValueError(f"Invalid matcher: {type(m)}") - return _matchers # type: ignore + return _matchers @staticmethod def to_listener_middleware( @@ -600,7 +600,7 @@

    Args

    ) else: raise ValueError(f"Invalid middleware: {type(m)}") - return _middleware # type: ignore
    + return _middleware

    Class variables

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html b/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html index 5131b2a3f..b8f52cb45 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html @@ -45,7 +45,7 @@

    Classes

    Expand source code -
    class WorkflowStepMiddleware(Middleware):  # type:ignore
    +
    class WorkflowStepMiddleware(Middleware):
         """Base middleware for step from app specific ones"""
     
         def __init__(self, step: WorkflowStep, listener_runner: ThreadListenerRunner):
    diff --git a/slack_bolt/version.py b/slack_bolt/version.py
    index c4d73f517..bf4428776 100644
    --- a/slack_bolt/version.py
    +++ b/slack_bolt/version.py
    @@ -1,3 +1,3 @@
     """Check the latest version at https://pypi.org/project/slack-bolt/"""
     
    -__version__ = "1.20.0"
    +__version__ = "1.20.1"
    
    From cee9455f73469cf714d8418dcea51f607ec824dc Mon Sep 17 00:00:00 2001
    From: Tracy Rericha <108959677+technically-tracy@users.noreply.github.com>
    Date: Wed, 28 Aug 2024 13:16:07 -0400
    Subject: [PATCH 004/282] minor change (#1140)
    
    ---
     docs/content/getting-started.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md
    index 394eb7c05..007e28ab8 100644
    --- a/docs/content/getting-started.md
    +++ b/docs/content/getting-started.md
    @@ -9,7 +9,7 @@ lang: en
     This guide is meant to walk you through getting up and running with a Slack app using Bolt for Python. Along the way, we’ll create a new Slack app, set up your local environment, and develop an app that listens and responds to messages from a Slack workspace.
     
     
    -When you're finished, you'll have this ⚡️[Getting Started with Slack app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started) to run, modify, and make your own.
    +When you're finished, you'll have this ⚡️[Getting Started with Slack app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started) to run, modify, and make your own. The possibilities are endless!
     
     :::info
     
    
    From 64eedeebfa1bfd766572215143e4e59d539fc6d2 Mon Sep 17 00:00:00 2001
    From: Tracy Rericha <108959677+technically-tracy@users.noreply.github.com>
    Date: Thu, 29 Aug 2024 15:28:04 -0400
    Subject: [PATCH 005/282] Docs: AI chatbot tutorial. (#1141)
    
    * tutorial content
    
    * link update
    
    * update name
    ---
     docs/content/tutorial/ai-chatbot.md | 203 ++++++++++++++++++++++++++++
     docs/sidebars.js                    |   3 +-
     docs/static/img/ai-chatbot/1.png    | Bin 0 -> 57012 bytes
     docs/static/img/ai-chatbot/2.png    | Bin 0 -> 68410 bytes
     docs/static/img/ai-chatbot/3.png    | Bin 0 -> 178957 bytes
     docs/static/img/ai-chatbot/4.png    | Bin 0 -> 59379 bytes
     docs/static/img/ai-chatbot/5.png    | Bin 0 -> 131658 bytes
     docs/static/img/ai-chatbot/6.png    | Bin 0 -> 47871 bytes
     docs/static/img/ai-chatbot/7.png    | Bin 0 -> 366040 bytes
     docs/static/img/ai-chatbot/8.png    | Bin 0 -> 294531 bytes
     10 files changed, 205 insertions(+), 1 deletion(-)
     create mode 100644 docs/content/tutorial/ai-chatbot.md
     create mode 100644 docs/static/img/ai-chatbot/1.png
     create mode 100644 docs/static/img/ai-chatbot/2.png
     create mode 100644 docs/static/img/ai-chatbot/3.png
     create mode 100644 docs/static/img/ai-chatbot/4.png
     create mode 100644 docs/static/img/ai-chatbot/5.png
     create mode 100644 docs/static/img/ai-chatbot/6.png
     create mode 100644 docs/static/img/ai-chatbot/7.png
     create mode 100644 docs/static/img/ai-chatbot/8.png
    
    diff --git a/docs/content/tutorial/ai-chatbot.md b/docs/content/tutorial/ai-chatbot.md
    new file mode 100644
    index 000000000..9fec871a0
    --- /dev/null
    +++ b/docs/content/tutorial/ai-chatbot.md
    @@ -0,0 +1,203 @@
    +# AI Chatbot
    +
    +In this tutorial, you'll learn how to bring the power of AI into your Slack workspace using a chatbot called Bolty that uses Anthropic or OpenAI. Here's what we'll do with this sample app:
    +
    +1. Create your app from an app manifest and clone a starter template
    +2. Set up and run your local project
    +3. Create a workflow using Workflow Builder to summarize messages in conversations
    +4. Select your preferred API and model to customize Bolty's responses
    +5. Interact with Bolty via direct message, the `/ask-bolty` slash command, or by mentioning the app in conversations
    +
    +## Prerequisites {#prereqs}
    +
    +Before getting started, you will need the following:
    +
    +* a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now—you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free.
    +* a development environment with [Python 3.6](https://www.python.org/downloads/) or later.
    +* an Anthropic or OpenAI account with sufficient credits, and in which you have generated a secret key.
    +
    +**Skip to the code**
    +If you'd rather skip the tutorial and just head straight to the code, you can use our [Bolt for Python AI Chatbot sample](https://github.com/slack-samples/bolt-python-ai-chatbot) as a template.
    +
    +## Creating your app {#create-app}
    +
    +1. Navigate to the [app creation page](https://api.slack.com/apps/new) and select **From a manifest**.
    +2. Select the workspace you want to install the application in.
    +3. Copy the contents of the [`manifest.json`](https://github.com/slack-samples/bolt-python-ai-chatbot/blob/main/manifest.json) file into the text box that says **Paste your manifest code here** (within the **JSON** tab) and click **Next**.
    +4. Review the configuration and click **Create**.
    +5. You're now in your app configuration's **Basic Information** page. Navigate to the **Install App** link in the left nav and click **Install to Workspace*, then **Allow** on the screen that follows.
    +
    +### Obtaining and storing your environment variables {#environment-variables}
    +
    +Before you'll be able to successfully run the app, you'll need to first obtain and set some environment variables.
    +
    +1. On the **Install App** page, copy your **Bot User OAuth Token**. You will store this in your environment as `SLACK_BOT_TOKEN` (we'll get to that next).
    +2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](https://api.slack.com/scopes/connections:write) scope, name the token, and click **Generate**. (For more details, refer to [understanding OAuth scopes for bots](https://api.slack.com/tutorials/tracks/understanding-oauth-scopes-bot)). Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`.
    +
    +To store your tokens and environment variables, run the following commands in the terminal. Replace the placeholder values with your bot and app tokens collected above, as well as the key or keys for the AI provider or providers you want to use:
    +
    +**For macOS**
    +```bash
    +export SLACK_BOT_TOKEN=
    +export SLACK_APP_TOKEN=
    +export OPENAI_API_KEY=
    +export ANTHROPIC_API_KEY=
    +```
    +
    +**For Windows**
    +```bash
    +set SLACK_BOT_TOKEN=
    +set SLACK_APP_TOKEN=
    +set OPENAI_API_KEY=
    +set ANTHROPIC_API_KEY=
    +```
    +
    +## Setting up and running your local project {#configure-project}
    +
    +Clone the starter template onto your machine by running the following command:
    +
    +```bash
    +git clone https://github.com/slack-samples/bolt-python-ai-chatbot.git
    +```
    +
    +Change into the new project directory:
    +
    +```bash
    +cd bolt-python-ai-chatbot
    +```
    +
    +Start your Python virtual environment:
    +
    +**For macOS**
    +```bash
    +python3 -m venv .venv
    +source .venv/bin/activate
    +```
    +
    +**For Windows**
    +```bash
    +py -m venv .venv
    +.venv\Scripts\activate
    +```
    +
    +Install the required dependencies:
    +
    +```bash
    +pip install -r requirements.txt
    +```
    +
    +Start your local server:
    +
    +```bash
    +python app.py
    +```
    +
    +If your app is up and running, you'll see a message that says "⚡️ Bolt app is running!"
    +
    +## Choosing your provider {#provider}
    +
    +Navigate to the Bolty **App Home** and select a provider from the drop-down menu. The options listed will be dependent on which secret keys you added when setting your environment variables.
    +
    +If you don't see Bolty listed under **Apps** in your workspace right away, never fear! You can mention **@Bolty** in a public channel to add the app, then navigate to your **App Home**.
    +
    +![Choose your AI provider](/img/ai-chatbot/6.png)
    +
    +## Setting up your workflow {#workflow}
    +
    +Within your development workspace, open Workflow Builder by clicking on your workspace name and then **Tools > Workflow Builder**. Select **New Workflow** > **Build Workflow**.
    +
    +Click **Untitled Workflow** at the top to rename your workflow. For this tutorial, we'll call the workflow **Welcome to the channel**. Enter a description, such as _Summarizes channels for new members_, and click **Save**.
    +
    +![Setting up a new workflow](/img/ai-chatbot/1.png)
    +
    +Select **Choose an event** under **Start the workflow...**, and then choose **When a person joins a channel**. Select the channel name from the drop-down menu and click **Save**.
    +
    +![Start the workflow](/img/ai-chatbot/2.png)
    +
    +Under **Then, do these things**, click **Add steps** and complete the following:
    +
    +1. Select **Messages** > **Send a message to a person**.
    +2. Under **Select a member**, choose **The user who joined the channel** from the drop-down menu.
    +3. Under **Add a message**, enter a short message, such as _Hi! Welcome to `{}The channel that the user joined`. Would you like a summary of the recent conversation?_ Note that the _`{}The channel that the user joined`_ is a variable; you can insert it by selecting **{}Insert a variable** at the bottom of the message text box.
    +4. Select the **Add Button** button, and name the button _Yes, give me a summary_. Click **Done**.
    +
    +![Send a message](/img/ai-chatbot/3.png)
    +
    +We'll add two more steps under the **Then, do these things** section. 
    +
    +First, scroll to the bottom of the list of steps and choose **Custom**, then choose **Bolty** and **Bolty Custom Function**. In the **Channel** drop-down menu, select **Channel that the user joined**. Click **Save**.
    +
    +![Bolty custom function](/img/ai-chatbot/4.png)
    +
    +For the final step, complete the following:
    +
    +1. Choose **Messages** and then **Send a message to a person**. Under **Select a member**, choose **Person who clicked the button** from the drop-down menu.
    +2. Under **Add a message**, click **Insert a variable** and choose **`{}Summary`** under the **Bolty Custom Function** section in the list that appears. Click **Save**.
    +
    +![Summary](/img/ai-chatbot/5.png)
    +
    +When finished, click **Finish Up**, then click **Publish** to make the workflow available in your workspace.
    +
    +## Interacting with Bolty {#interact}
    +
    +### Summarizing recent conversations {#summarize}
    +
    +In order for Bolty to provide summaries of recent conversation in a channel, Bolty _must_ be a member of that channel. 
    +
    +1. Invite Bolty to a channel that you are able to leave and rejoin (for example, not the **#general** channel or a private channel someone else created) by mentioning the app in the channel—i.e., tagging **@Bolty** in the channel and sending your message.
    +2. Slackbot will prompt you to either invite Bolty to the channel, or do nothing. Click **Invite Them**. Now when new users join the channel, the workflow you just created will be kicked off.
    +
    +To test this, leave the channel you just invited Bolty to and rejoin it. This will kick off your workflow and you'll receive a direct message from **Welcome to the channel**. Click the **Yes, give me a summary** button, and Bolty will summarize the recent conversations in the channel you joined.
    +
    +![Channel summary](/img/ai-chatbot/7.png)
    +
    +The central part of this functionality is shown in the following code snippet. Note the use of the [`user_context`](https://api.slack.com/automation/types#usercontext) object, a Slack type that represents the user who is interacting with our workflow, as well as the `history` of the channel that will be summarized, which includes the ten most recent messages.
    +
    +```python
    +from ai.providers import get_provider_response
    +from logging import Logger
    +from slack_bolt import Complete, Fail, Ack
    +from slack_sdk import WebClient
    +from ..listener_utils.listener_constants import SUMMARIZE_CHANNEL_WORKFLOW
    +from ..listener_utils.parse_conversation import parse_conversation
    +
    +"""
    +Handles the event to summarize a Slack channel's conversation history.
    +It retrieves the conversation history, parses it, generates a summary using an AI response,
    +and completes the workflow with the summary or fails if an error occurs.
    +"""
    +
    +
    +def handle_summary_function_callback(
    +    ack: Ack, inputs: dict, fail: Fail, logger: Logger, client: WebClient, complete: Complete
    +):
    +    ack()
    +    try:
    +        user_context = inputs["user_context"]
    +        channel_id = inputs["channel_id"]
    +        history = client.conversations_history(channel=channel_id, limit=10)["messages"]
    +        conversation = parse_conversation(history)
    +
    +        summary = get_provider_response(user_context["id"], SUMMARIZE_CHANNEL_WORKFLOW, conversation)
    +
    +        complete({"user_context": user_context, "response": summary})
    +    except Exception as e:
    +        logger.exception(e)
    +        fail(e)
    +```
    +
    +### Asking Bolty a question {#ask-app}
    +
    +To ask Bolty a question, you can chat with Bolty in any channel the app is in. Use the `\ask-bolty` slash command to provide a prompt for Bolty to answer. Note that Bolty is currently not supported in threads.
    +
    +You can also navigate to **Bolty** in your **Apps** list and select the **Messages** tab to chat with Bolty directly. 
    +
    +![Ask Bolty](/img/ai-chatbot/8.png)
    +
    +## Next steps {#next-steps}
    +
    +Congratulations! You've successfully integrated the power of AI into your workspace. Check out these links to take the next steps in your Bolt for Python journey.
    +
    +* To learn more about Bolt for Python, refer to the [Getting started](../getting-started) documentation.
    +* For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](https://api.slack.com/automation/functions/custom-bolt) guide.
    +* To use the Bolt for Python SDK to develop on the automations platform, refer to the [Create a workflow step for Workflow Builder: Bolt for Python](https://api.slack.com/tutorials/tracks/bolt-custom-function) tutorial.
    diff --git a/docs/sidebars.js b/docs/sidebars.js
    index f2f84b7b4..609baa312 100644
    --- a/docs/sidebars.js
    +++ b/docs/sidebars.js
    @@ -81,7 +81,8 @@ const sidebars = {
           type: 'category',
           label: 'Tutorials',
           items: [
    -        'tutorial/getting-started-http'
    +        'tutorial/getting-started-http',
    +        'tutorial/ai-chatbot'
           ],
         },
         { type: 'html', value: '
    ' }, diff --git a/docs/static/img/ai-chatbot/1.png b/docs/static/img/ai-chatbot/1.png new file mode 100644 index 0000000000000000000000000000000000000000..7198bc23530577aa62718b37e489e0f3d61b1223 GIT binary patch literal 57012 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/static/img/ai-chatbot/3.png b/docs/static/img/ai-chatbot/3.png new file mode 100644 index 0000000000000000000000000000000000000000..fbf795ad804cc44eee0ef3106149741827088539 GIT binary patch literal 178957 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

    @&MfU21VfL(_?Tlx@sQ~3yi1D7 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 Kv-*c#!2bc#hQApA literal 0 HcmV?d00001 From 81889560b3312fab468894553fe14fd1525d05d4 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Sat, 31 Aug 2024 01:00:31 +0900 Subject: [PATCH 006/282] Add listener_runner to context object to enable developers to leverage lazy listeners in middleware (#1142) * Add listener_runner to context object to enable developers to leverage lazy listeners in middleware * mypy lint fix --- scripts/run_tests.sh | 3 +- slack_bolt/app/app.py | 6 +- slack_bolt/app/async_app.py | 6 +- slack_bolt/context/async_context.py | 13 +++- slack_bolt/context/base_context.py | 7 +- slack_bolt/context/context.py | 11 ++- slack_bolt/listener/asyncio_runner.py | 7 +- slack_bolt/listener/thread_runner.py | 7 +- .../workflows/step/async_step_middleware.py | 8 +- slack_bolt/workflows/step/step_middleware.py | 8 +- tests/scenario_tests/test_middleware.py | 70 +++++++++++++++++- tests/scenario_tests_async/test_middleware.py | 73 +++++++++++++++++++ 12 files changed, 192 insertions(+), 27 deletions(-) diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index 8f8449018..e4cc99709 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -14,6 +14,5 @@ then black slack_bolt/ tests/ && \ pytest -vv $1 else - black slack_bolt/ tests/ && pytest - fi + black slack_bolt/ tests/ && pytest fi diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index 3fefac341..c72394821 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -735,7 +735,7 @@ def step( elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") - self.use(WorkflowStepMiddleware(step, self.listener_runner)) + self.use(WorkflowStepMiddleware(step)) # ------------------------- # global error handler @@ -1350,6 +1350,10 @@ def _init_context(self, req: BoltRequest): ) req.context["client"] = client_per_request + # Most apps do not need this "listener_runner" instance. + # It is intended for apps that start lazy listeners from their custom global middleware. + req.context["listener_runner"] = self.listener_runner + @staticmethod def _to_listener_functions( kwargs: dict, diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 8f66e3ba3..92bad71b7 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -761,7 +761,7 @@ def step( elif not isinstance(step, AsyncWorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") - self.use(AsyncWorkflowStepMiddleware(step, self._async_listener_runner)) + self.use(AsyncWorkflowStepMiddleware(step)) # ------------------------- # global error handler @@ -1390,6 +1390,10 @@ def _init_context(self, req: AsyncBoltRequest): ) req.context["client"] = client_per_request + # Most apps do not need this "listener_runner" instance. + # It is intended for apps that start lazy listeners from their custom global middleware. + req.context["listener_runner"] = self.listener_runner + @staticmethod def _to_listener_functions( kwargs: dict, diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index 9381cdd3c..cd051fa31 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -17,20 +17,29 @@ class AsyncBoltContext(BaseContext): def to_copyable(self) -> "AsyncBoltContext": new_dict = {} for prop_name, prop_value in self.items(): - if prop_name in self.standard_property_names: + if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value + elif prop_name in self.non_copyable_standard_property_names: + # Do nothing with this property (e.g., listener_runner) + continue else: try: copied_value = create_copy(prop_value) new_dict[prop_name] = copied_value except TypeError as te: self.logger.debug( - f"Skipped settings '{prop_name}' to a copied request for lazy listeners " + f"Skipped setting '{prop_name}' to a copied request for lazy listeners " f"as it's not possible to make a deep copy (error: {te})" ) return AsyncBoltContext(new_dict) + # The return type is intentionally string to avoid circular imports + @property + def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defined] + """The properly configured listener_runner that is available for middleware/listeners.""" + return self["listener_runner"] + @property def client(self) -> Optional[AsyncWebClient]: """The `AsyncWebClient` instance available for this request. diff --git a/slack_bolt/context/base_context.py b/slack_bolt/context/base_context.py index c85177664..2c00d8082 100644 --- a/slack_bolt/context/base_context.py +++ b/slack_bolt/context/base_context.py @@ -7,7 +7,7 @@ class BaseContext(dict): """Context object associated with a request from Slack.""" - standard_property_names = [ + copyable_standard_property_names = [ "logger", "token", "enterprise_id", @@ -35,6 +35,11 @@ class BaseContext(dict): "complete", "fail", ] + non_copyable_standard_property_names = [ + "listener_runner", + ] + + standard_property_names = copyable_standard_property_names + non_copyable_standard_property_names @property def logger(self) -> Logger: diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index 8faf8bd27..4c78c7fea 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -17,9 +17,12 @@ class BoltContext(BaseContext): def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): - if prop_name in self.standard_property_names: + if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value + elif prop_name in self.non_copyable_standard_property_names: + # Do nothing with this property (e.g., listener_runner) + continue else: try: copied_value = create_copy(prop_value) @@ -32,6 +35,12 @@ def to_copyable(self) -> "BoltContext": ) return BoltContext(new_dict) + # The return type is intentionally string to avoid circular imports + @property + def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + """The properly configured listener_runner that is available for middleware/listeners.""" + return self["listener_runner"] + @property def client(self) -> Optional[WebClient]: """The `WebClient` instance available for this request. diff --git a/slack_bolt/listener/asyncio_runner.py b/slack_bolt/listener/asyncio_runner.py index 04f6b038e..01e8641ed 100644 --- a/slack_bolt/listener/asyncio_runner.py +++ b/slack_bolt/listener/asyncio_runner.py @@ -174,12 +174,11 @@ def _start_lazy_function(self, lazy_func: Callable[..., Awaitable[None]], reques copied_request = self._build_lazy_request(request, func_name) self.lazy_listener_runner.start(function=lazy_func, request=copied_request) - @staticmethod - def _build_lazy_request(request: AsyncBoltRequest, lazy_func_name: str) -> AsyncBoltRequest: - copied_request = create_copy(request.to_copyable()) - copied_request.method = "NONE" + def _build_lazy_request(self, request: AsyncBoltRequest, lazy_func_name: str) -> AsyncBoltRequest: + copied_request: AsyncBoltRequest = create_copy(request.to_copyable()) copied_request.lazy_only = True copied_request.lazy_function_name = lazy_func_name + copied_request.context["listener_runner"] = self return copied_request def _debug_log_completion(self, starting_time: float, response: BoltResponse) -> None: diff --git a/slack_bolt/listener/thread_runner.py b/slack_bolt/listener/thread_runner.py index 4821fb70b..c2d87b3d5 100644 --- a/slack_bolt/listener/thread_runner.py +++ b/slack_bolt/listener/thread_runner.py @@ -185,12 +185,11 @@ def _start_lazy_function(self, lazy_func: Callable[..., None], request: BoltRequ copied_request = self._build_lazy_request(request, func_name) self.lazy_listener_runner.start(function=lazy_func, request=copied_request) - @staticmethod - def _build_lazy_request(request: BoltRequest, lazy_func_name: str) -> BoltRequest: - copied_request = create_copy(request.to_copyable()) - copied_request.method = "NONE" + def _build_lazy_request(self, request: BoltRequest, lazy_func_name: str) -> BoltRequest: + copied_request: BoltRequest = create_copy(request.to_copyable()) copied_request.lazy_only = True copied_request.lazy_function_name = lazy_func_name + copied_request.context["listener_runner"] = self return copied_request def _debug_log_completion(self, starting_time: float, response: BoltResponse) -> None: diff --git a/slack_bolt/workflows/step/async_step_middleware.py b/slack_bolt/workflows/step/async_step_middleware.py index 62b3d1afe..5801a51e6 100644 --- a/slack_bolt/workflows/step/async_step_middleware.py +++ b/slack_bolt/workflows/step/async_step_middleware.py @@ -2,7 +2,6 @@ from typing import Callable, Optional, Awaitable from slack_bolt.listener.async_listener import AsyncListener -from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner from slack_bolt.middleware.async_middleware import AsyncMiddleware from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse @@ -13,9 +12,8 @@ class AsyncWorkflowStepMiddleware(AsyncMiddleware): """Base middleware for step from app specific ones""" - def __init__(self, step: AsyncWorkflowStep, listener_runner: AsyncioListenerRunner): + def __init__(self, step: AsyncWorkflowStep): self.step = step - self.listener_runner = listener_runner async def async_process( self, @@ -40,8 +38,8 @@ async def async_process( return await next() + @staticmethod async def _run( - self, listener: AsyncListener, req: AsyncBoltRequest, resp: BoltResponse, @@ -50,7 +48,7 @@ async def _run( if next_was_not_called: return None - return await self.listener_runner.run( + return await req.context.listener_runner.run( request=req, response=resp, listener_name=get_name_for_callable(listener.ack_function), diff --git a/slack_bolt/workflows/step/step_middleware.py b/slack_bolt/workflows/step/step_middleware.py index 2ea6194d1..59af001a7 100644 --- a/slack_bolt/workflows/step/step_middleware.py +++ b/slack_bolt/workflows/step/step_middleware.py @@ -2,7 +2,6 @@ from typing import Callable, Optional from slack_bolt.listener import Listener -from slack_bolt.listener.thread_runner import ThreadListenerRunner from slack_bolt.middleware import Middleware from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse @@ -13,9 +12,8 @@ class WorkflowStepMiddleware(Middleware): """Base middleware for step from app specific ones""" - def __init__(self, step: WorkflowStep, listener_runner: ThreadListenerRunner): + def __init__(self, step: WorkflowStep): self.step = step - self.listener_runner = listener_runner def process( self, @@ -43,8 +41,8 @@ def process( return next() + @staticmethod def _run( - self, listener: Listener, req: BoltRequest, resp: BoltResponse, @@ -53,7 +51,7 @@ def _run( if next_was_not_called: return None - return self.listener_runner.run( + return req.context.listener_runner.run( request=req, response=resp, listener_name=get_name_for_callable(listener.ack_function), diff --git a/tests/scenario_tests/test_middleware.py b/tests/scenario_tests/test_middleware.py index aa16bf620..6553445df 100644 --- a/tests/scenario_tests/test_middleware.py +++ b/tests/scenario_tests/test_middleware.py @@ -1,15 +1,23 @@ import json -from time import time +import logging +from time import time, sleep +from typing import Callable, Optional from slack_sdk.signature import SignatureVerifier from slack_sdk.web import WebClient +from slack_bolt import BoltResponse, CustomListenerMatcher from slack_bolt.app import App +from slack_bolt.listener import CustomListener +from slack_bolt.listener.thread_runner import ThreadListenerRunner +from slack_bolt.middleware import Middleware from slack_bolt.request import BoltRequest +from slack_bolt.request.payload_utils import is_shortcut from tests.mock_web_api_server import ( setup_mock_web_api_server, cleanup_mock_web_api_server, assert_auth_test_count, + assert_received_request_count, ) from tests.utils import remove_os_env_temporarily, restore_os_env @@ -168,6 +176,27 @@ def __call__(self, next_): assert response.body == "acknowledged!" assert_auth_test_count(self, 1) + def test_lazy_listener_middleware(self): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + unmatch_middleware = LazyListenerStarter("xxxx") + app.use(unmatch_middleware) + + response = app.dispatch(self.build_request()) + assert response.status == 404 + assert_auth_test_count(self, 1) + + my_middleware = LazyListenerStarter("test-shortcut") + app.use(my_middleware) + response = app.dispatch(self.build_request()) + assert response.status == 200 + count = 0 + while count < 20 and my_middleware.lazy_called is False: + sleep(0.05) + assert my_middleware.lazy_called is True + def just_ack(ack): ack("acknowledged!") @@ -183,3 +212,42 @@ def just_next(next): def just_next_(next_): next_() + + +class LazyListenerStarter(Middleware): + lazy_called: bool + callback_id: str + + def __init__(self, callback_id: str): + self.lazy_called = False + self.callback_id = callback_id + + def lazy_listener(self): + self.lazy_called = True + + def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: + if is_shortcut(req.body): + listener = CustomListener( + app_name="test-app", + ack_function=just_ack, + lazy_functions=[self.lazy_listener], + matchers=[ + CustomListenerMatcher( + app_name="test-app", + func=lambda payload: payload.get("callback_id") == self.callback_id, + ) + ], + middleware=[], + base_logger=req.context.logger, + ) + if listener.matches(req=req, resp=resp): + listener_runner: ThreadListenerRunner = req.context.listener_runner + response = listener_runner.run( + request=req, + response=resp, + listener_name="test", + listener=listener, + ) + if response is not None: + return response + next() diff --git a/tests/scenario_tests_async/test_middleware.py b/tests/scenario_tests_async/test_middleware.py index 694a7316e..6272f17e4 100644 --- a/tests/scenario_tests_async/test_middleware.py +++ b/tests/scenario_tests_async/test_middleware.py @@ -1,12 +1,20 @@ import json +import asyncio from time import time +from typing import Callable, Awaitable, Optional import pytest from slack_sdk.signature import SignatureVerifier from slack_sdk.web.async_client import AsyncWebClient +from slack_bolt import BoltResponse +from slack_bolt.listener.async_listener import AsyncCustomListener +from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner +from slack_bolt.listener_matcher.async_listener_matcher import AsyncCustomListenerMatcher +from slack_bolt.middleware.async_middleware import AsyncMiddleware from slack_bolt.app.async_app import AsyncApp from slack_bolt.request.async_request import AsyncBoltRequest +from slack_bolt.request.payload_utils import is_shortcut from tests.mock_web_api_server import ( cleanup_mock_web_api_server_async, assert_auth_test_count_async, @@ -145,6 +153,27 @@ async def just_next_(next_): assert response.body == "acknowledged!" await assert_auth_test_count_async(self, 1) + @pytest.mark.asyncio + async def test_lazy_listener_middleware(self): + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + ) + unmatch_middleware = LazyListenerStarter("xxxx") + app.use(unmatch_middleware) + + response = await app.async_dispatch(self.build_request()) + assert response.status == 404 + + my_middleware = LazyListenerStarter("test-shortcut") + app.use(my_middleware) + response = await app.async_dispatch(self.build_request()) + assert response.status == 200 + count = 0 + while count < 20 and my_middleware.lazy_called is False: + await asyncio.sleep(0.05) + assert my_middleware.lazy_called is True + async def just_ack(ack): await ack("acknowledged!") @@ -160,3 +189,47 @@ async def just_next(next): async def just_next_(next_): await next_() + + +class LazyListenerStarter(AsyncMiddleware): + lazy_called: bool + callback_id: str + + def __init__(self, callback_id: str): + self.lazy_called = False + self.callback_id = callback_id + + async def lazy_listener(self): + self.lazy_called = True + + async def async_process( + self, *, req: AsyncBoltRequest, resp: BoltResponse, next: Callable[[], Awaitable[BoltResponse]] + ) -> Optional[BoltResponse]: + async def is_target(payload: dict): + return payload.get("callback_id") == self.callback_id + + if is_shortcut(req.body): + listener = AsyncCustomListener( + app_name="test-app", + ack_function=just_ack, + lazy_functions=[self.lazy_listener], + matchers=[ + AsyncCustomListenerMatcher( + app_name="test-app", + func=is_target, + ) + ], + middleware=[], + base_logger=req.context.logger, + ) + if await listener.async_matches(req=req, resp=resp): + listener_runner: AsyncioListenerRunner = req.context.listener_runner + response = await listener_runner.run( + request=req, + response=resp, + listener_name="test", + listener=listener, + ) + if response is not None: + return response + await next() From aedac2cdc3c2ace4d34d19434734dcbed293f4db Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 2 Sep 2024 02:21:39 +0000 Subject: [PATCH 007/282] disc: remove the optional typing of client (#1143) --- slack_bolt/authorization/async_authorize.py | 4 ++-- slack_bolt/authorization/async_authorize_args.py | 2 +- slack_bolt/authorization/authorize.py | 4 ++-- slack_bolt/authorization/authorize_args.py | 2 +- slack_bolt/context/async_context.py | 10 +++++----- slack_bolt/context/context.py | 10 +++++----- .../async_attaching_function_token.py | 2 +- .../attaching_function_token.py | 2 +- .../authorization/async_multi_teams_authorization.py | 2 +- .../authorization/async_single_team_authorization.py | 4 ++-- .../authorization/multi_teams_authorization.py | 2 +- .../authorization/single_team_authorization.py | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/slack_bolt/authorization/async_authorize.py b/slack_bolt/authorization/async_authorize.py index 75228f6dc..f3303e429 100644 --- a/slack_bolt/authorization/async_authorize.py +++ b/slack_bolt/authorization/async_authorize.py @@ -331,10 +331,10 @@ async def __call__( return self.authorize_result_cache[token] try: - auth_test_api_response = await context.client.auth_test(token=token) # type: ignore[union-attr] + auth_test_api_response = await context.client.auth_test(token=token) user_auth_test_response = None if user_token is not None and token != user_token: - user_auth_test_response = await context.client.auth_test(token=user_token) # type: ignore[union-attr] + user_auth_test_response = await context.client.auth_test(token=user_token) authorize_result = AuthorizeResult.from_auth_test_response( auth_test_response=auth_test_api_response, user_auth_test_response=user_auth_test_response, diff --git a/slack_bolt/authorization/async_authorize_args.py b/slack_bolt/authorization/async_authorize_args.py index c6a111982..08af16766 100644 --- a/slack_bolt/authorization/async_authorize_args.py +++ b/slack_bolt/authorization/async_authorize_args.py @@ -32,7 +32,7 @@ def __init__( """ self.context = context self.logger = context.logger - self.client = context.client # type: ignore[assignment] + self.client = context.client self.enterprise_id = enterprise_id self.team_id = team_id self.user_id = user_id diff --git a/slack_bolt/authorization/authorize.py b/slack_bolt/authorization/authorize.py index c6fbe752e..afed6fa8b 100644 --- a/slack_bolt/authorization/authorize.py +++ b/slack_bolt/authorization/authorize.py @@ -328,10 +328,10 @@ def __call__( return self.authorize_result_cache[token] try: - auth_test_api_response = context.client.auth_test(token=token) # type: ignore[union-attr] + auth_test_api_response = context.client.auth_test(token=token) user_auth_test_response = None if user_token is not None and token != user_token: - user_auth_test_response = context.client.auth_test(token=user_token) # type: ignore[union-attr] + user_auth_test_response = context.client.auth_test(token=user_token) authorize_result = AuthorizeResult.from_auth_test_response( auth_test_response=auth_test_api_response, user_auth_test_response=user_auth_test_response, diff --git a/slack_bolt/authorization/authorize_args.py b/slack_bolt/authorization/authorize_args.py index b488dfefc..2d436b697 100644 --- a/slack_bolt/authorization/authorize_args.py +++ b/slack_bolt/authorization/authorize_args.py @@ -32,7 +32,7 @@ def __init__( """ self.context = context self.logger = context.logger - self.client = context.client # type: ignore[assignment] + self.client = context.client self.enterprise_id = enterprise_id self.team_id = team_id self.user_id = user_id diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index cd051fa31..49748366c 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -41,7 +41,7 @@ def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defin return self["listener_runner"] @property - def client(self) -> Optional[AsyncWebClient]: + def client(self) -> AsyncWebClient: """The `AsyncWebClient` instance available for this request. @app.event("app_mention") @@ -129,8 +129,8 @@ async def handle_button_clicks(ack, respond): if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"] @@ -156,7 +156,7 @@ async def handle_button_clicks(context): """ if "complete" not in self: self["complete"] = AsyncComplete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + client=self.client, function_execution_id=self.function_execution_id ) return self["complete"] @@ -182,6 +182,6 @@ async def handle_button_clicks(context): """ if "fail" not in self: self["fail"] = AsyncFail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + client=self.client, function_execution_id=self.function_execution_id ) return self["fail"] diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index 4c78c7fea..7d20e3a07 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -42,7 +42,7 @@ def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-define return self["listener_runner"] @property - def client(self) -> Optional[WebClient]: + def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") @@ -130,8 +130,8 @@ def handle_button_clicks(ack, respond): if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"] @@ -157,7 +157,7 @@ def handle_button_clicks(context): """ if "complete" not in self: self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + client=self.client, function_execution_id=self.function_execution_id ) return self["complete"] @@ -183,6 +183,6 @@ def handle_button_clicks(context): """ if "fail" not in self: self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] + client=self.client, function_execution_id=self.function_execution_id ) return self["fail"] diff --git a/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.py b/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.py index 98b2309f5..434133e7b 100644 --- a/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.py +++ b/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.py @@ -15,6 +15,6 @@ async def async_process( next: Callable[[], Awaitable[BoltResponse]], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] + req.context.client.token = req.context.function_bot_access_token return await next() diff --git a/slack_bolt/middleware/attaching_function_token/attaching_function_token.py b/slack_bolt/middleware/attaching_function_token/attaching_function_token.py index 53fe3742f..aea4a77a1 100644 --- a/slack_bolt/middleware/attaching_function_token/attaching_function_token.py +++ b/slack_bolt/middleware/attaching_function_token/attaching_function_token.py @@ -15,6 +15,6 @@ def process( next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] + req.context.client.token = req.context.function_bot_access_token return next() diff --git a/slack_bolt/middleware/authorization/async_multi_teams_authorization.py b/slack_bolt/middleware/authorization/async_multi_teams_authorization.py index 1b3dd37cb..592431f0f 100644 --- a/slack_bolt/middleware/authorization/async_multi_teams_authorization.py +++ b/slack_bolt/middleware/authorization/async_multi_teams_authorization.py @@ -86,7 +86,7 @@ async def async_process( req.context["token"] = token # As AsyncApp#_init_context() generates a new AsyncWebClient for this request, # it's safe to modify this instance. - req.context.client.token = token # type: ignore[union-attr] + req.context.client.token = token return await next() else: # This situation can arise if: diff --git a/slack_bolt/middleware/authorization/async_single_team_authorization.py b/slack_bolt/middleware/authorization/async_single_team_authorization.py index 4f921834c..c783ce4ce 100644 --- a/slack_bolt/middleware/authorization/async_single_team_authorization.py +++ b/slack_bolt/middleware/authorization/async_single_team_authorization.py @@ -50,13 +50,13 @@ async def async_process( try: if self.auth_test_result is None: - self.auth_test_result = await req.context.client.auth_test() # type: ignore[union-attr] + self.auth_test_result = await req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, # type: ignore[union-attr] + token=req.context.client.token, request_user_id=req.context.user_id, ) ) diff --git a/slack_bolt/middleware/authorization/multi_teams_authorization.py b/slack_bolt/middleware/authorization/multi_teams_authorization.py index a116abbf7..ee8896ea3 100644 --- a/slack_bolt/middleware/authorization/multi_teams_authorization.py +++ b/slack_bolt/middleware/authorization/multi_teams_authorization.py @@ -89,7 +89,7 @@ def process( req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. - req.context.client.token = token # type: ignore[union-attr] + req.context.client.token = token return next() else: # This situation can arise if: diff --git a/slack_bolt/middleware/authorization/single_team_authorization.py b/slack_bolt/middleware/authorization/single_team_authorization.py index 7b70f299c..80a864b4e 100644 --- a/slack_bolt/middleware/authorization/single_team_authorization.py +++ b/slack_bolt/middleware/authorization/single_team_authorization.py @@ -62,13 +62,13 @@ def process( try: if not self.auth_test_result: - self.auth_test_result = req.context.client.auth_test() # type: ignore[union-attr] + self.auth_test_result = req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, # type: ignore[union-attr] + token=req.context.client.token, request_user_id=req.context.user_id, ) ) From 5a9e2cb4670f371ff14de151e79bfa7ad911084d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:55:17 +0900 Subject: [PATCH 008/282] Bump webpack from 5.92.1 to 5.94.0 in /docs (#1144) Bumps [webpack](https://github.com/webpack/webpack) from 5.92.1 to 5.94.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.92.1...v5.94.0) --- updated-dependencies: - dependency-name: webpack dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 14c4c2fcd..f679ec7d4 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -3321,24 +3321,6 @@ "@types/ms": "*" } }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -5717,9 +5699,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -14061,11 +14043,10 @@ } }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -14074,7 +14055,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", From a09f26761c0eedcc8feb2043e5773d892caa63f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:55:41 +0900 Subject: [PATCH 009/282] Bump mypy from 1.11.1 to 1.11.2 (#1153) Bumps [mypy](https://github.com/python/mypy) from 1.11.1 to 1.11.2. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.11.1...v1.11.2) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 8c721ec65..723142103 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.11.1 +mypy==1.11.2 flake8==6.0.0 black==22.8.0 # Until we drop Python 3.6 support, we have to stay with this version From 2578f5ae157b9914ce19ea90afe340f531cff725 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:56:02 +0900 Subject: [PATCH 010/282] Update gunicorn requirement from <23,>=20 to >=20,<24 (#1151) Updates the requirements on [gunicorn](https://github.com/benoitc/gunicorn) to permit the latest version. - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/20.0.0...23.0.0) --- updated-dependencies: - dependency-name: gunicorn dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/adapter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index d35a5f2cf..1dc0d5d60 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -18,5 +18,5 @@ sanic>=22,<24; python_version>"3.6" starlette>=0.14,<1 tornado>=6,<7 uvicorn<1 # The oldest version can vary among Python runtime versions -gunicorn>=20,<23 +gunicorn>=20,<24 websocket_client>=1.2.3,<2 # Socket Mode 3rd party implementation From 4b22121d05f6c5f8ed13340b970f580da8d48210 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:56:11 +0900 Subject: [PATCH 011/282] Update websockets requirement from <13 to <14 (#1150) Updates the requirements on [websockets](https://github.com/python-websockets/websockets) to permit the latest version. - [Release notes](https://github.com/python-websockets/websockets/releases) - [Commits](https://github.com/python-websockets/websockets/compare/1.0...13.0.1) --- updated-dependencies: - dependency-name: websockets dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/async.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/async.txt b/requirements/async.txt index dd105eb8b..54e62ca94 100644 --- a/requirements/async.txt +++ b/requirements/async.txt @@ -1,3 +1,3 @@ # pip install -r requirements/async.txt aiohttp>=3,<4 -websockets<13 +websockets<14 From 80fdd2b135145057769c842cc32ee02a952b448a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:56:26 +0900 Subject: [PATCH 012/282] Bump black from 22.8.0 to 24.8.0 (#1149) Bumps [black](https://github.com/psf/black) from 22.8.0 to 24.8.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.8.0...24.8.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 723142103..38c4d6930 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ mypy==1.11.2 flake8==6.0.0 -black==22.8.0 # Until we drop Python 3.6 support, we have to stay with this version +black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version From 0ecf8e64709d23520a9bcf7b27506ffd2af9a52d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:00:27 +0900 Subject: [PATCH 013/282] Bump prism-react-renderer from 2.3.1 to 2.4.0 in /docs (#1147) Bumps [prism-react-renderer](https://github.com/FormidableLabs/prism-react-renderer) from 2.3.1 to 2.4.0. - [Release notes](https://github.com/FormidableLabs/prism-react-renderer/releases) - [Commits](https://github.com/FormidableLabs/prism-react-renderer/compare/prism-react-renderer@2.3.1...prism-react-renderer@2.4.0) --- updated-dependencies: - dependency-name: prism-react-renderer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 8 ++++---- docs/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index f679ec7d4..6b4c7de05 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -14,7 +14,7 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", - "prism-react-renderer": "^2.3.0", + "prism-react-renderer": "^2.4.0", "react": "^18.0.0", "react-dom": "^18.0.0" }, @@ -11512,9 +11512,9 @@ } }, "node_modules/prism-react-renderer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz", - "integrity": "sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.0.tgz", + "integrity": "sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==", "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" diff --git a/docs/package.json b/docs/package.json index 4cd941ca4..a3dc6f212 100644 --- a/docs/package.json +++ b/docs/package.json @@ -20,7 +20,7 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", - "prism-react-renderer": "^2.3.0", + "prism-react-renderer": "^2.4.0", "react": "^18.0.0", "react-dom": "^18.0.0" }, From c57a6fc87dadb4f12d1e47f7bb331daabdf97806 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:05:12 +0900 Subject: [PATCH 014/282] Bump @docusaurus/module-type-aliases from 3.4.0 to 3.5.2 in /docs (#1146) Bumps [@docusaurus/module-type-aliases](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-module-type-aliases) from 3.4.0 to 3.5.2. - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.5.2/packages/docusaurus-module-type-aliases) --- updated-dependencies: - dependency-name: "@docusaurus/module-type-aliases" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 86 +++++++++++++++++++++++++++++++++++++++--- docs/package.json | 2 +- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 6b4c7de05..2cc72732b 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -19,7 +19,7 @@ "react-dom": "^18.0.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/module-type-aliases": "3.5.2", "@docusaurus/types": "3.4.0" }, "engines": { @@ -2291,11 +2291,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz", + "integrity": "sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==", + "dev": true, "dependencies": { - "@docusaurus/types": "3.4.0", + "@docusaurus/types": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2308,6 +2309,27 @@ "react-dom": "*" } }, + "node_modules/@docusaurus/module-type-aliases/node_modules/@docusaurus/types": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz", + "integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==", + "dev": true, + "dependencies": { + "@mdx-js/mdx": "^3.0.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.9.2", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1", + "webpack-merge": "^5.9.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@docusaurus/plugin-client-redirects": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.4.0.tgz", @@ -2392,6 +2414,24 @@ "react-dom": "^18.0.0" } }, + "node_modules/@docusaurus/plugin-content-docs/node_modules/@docusaurus/module-type-aliases": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", + "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "dependencies": { + "@docusaurus/types": "3.4.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, "node_modules/@docusaurus/plugin-content-pages": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", @@ -2578,6 +2618,24 @@ "react-dom": "^18.0.0" } }, + "node_modules/@docusaurus/theme-classic/node_modules/@docusaurus/module-type-aliases": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", + "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "dependencies": { + "@docusaurus/types": "3.4.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, "node_modules/@docusaurus/theme-common": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", @@ -2607,6 +2665,24 @@ "react-dom": "^18.0.0" } }, + "node_modules/@docusaurus/theme-common/node_modules/@docusaurus/module-type-aliases": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", + "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "dependencies": { + "@docusaurus/types": "3.4.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, "node_modules/@docusaurus/theme-search-algolia": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", diff --git a/docs/package.json b/docs/package.json index a3dc6f212..f8729aa15 100644 --- a/docs/package.json +++ b/docs/package.json @@ -25,7 +25,7 @@ "react-dom": "^18.0.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/module-type-aliases": "3.5.2", "@docusaurus/types": "3.4.0" }, "browserslist": { From b3b05a90548cb460e389b330b518a13358125957 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:05:23 +0900 Subject: [PATCH 015/282] Bump micromatch from 4.0.7 to 4.0.8 in /docs (#1154) Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.7 to 4.0.8. - [Release notes](https://github.com/micromatch/micromatch/releases) - [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/micromatch/compare/4.0.7...4.0.8) --- updated-dependencies: - dependency-name: micromatch dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 2cc72732b..05fe27113 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -10290,9 +10290,9 @@ ] }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" From 53f30a1399c83c61966b5dcd537c58328094648c Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 3 Sep 2024 13:55:21 +0000 Subject: [PATCH 016/282] chore: group docusaurus dependencies (#1155) --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8ea06fc7d..8cc5d1809 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,3 +16,7 @@ updates: directory: "/docs" schedule: interval: "monthly" + groups: + docusaurus: + patterns: + - "@docusaurus/*" From 7ab47a42364ba75bb758d00b61f86d6410ae7ab6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:02:40 +0000 Subject: [PATCH 017/282] Bump the docusaurus group in /docs with 4 updates (#1156) --- docs/package-lock.json | 667 ++++++++++++++++++++++------------------- docs/package.json | 8 +- 2 files changed, 365 insertions(+), 310 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 05fe27113..6a98f1108 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,9 +8,9 @@ "name": "website", "version": "2024.08.01", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-client-redirects": "^3.4.0", - "@docusaurus/preset-classic": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/plugin-client-redirects": "^3.5.2", + "@docusaurus/preset-classic": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -20,7 +20,7 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "3.5.2", - "@docusaurus/types": "3.4.0" + "@docusaurus/types": "3.5.2" }, "engines": { "node": ">=20.0" @@ -98,6 +98,25 @@ "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, "node_modules/@algolia/client-analytics": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.24.0.tgz", @@ -109,7 +128,7 @@ "@algolia/transporter": "4.24.0" } }, - "node_modules/@algolia/client-common": { + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", @@ -118,6 +137,25 @@ "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.2.4.tgz", + "integrity": "sha512-xNkNJ9Vk1WjxEU/SzcA2vZWeYSiQFQOUS7Akffx8aeAIJIOcmwbpLr2D8JzBEC4QNmNb5KAZOJTrGl1ri9Mclg==", + "peer": true, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/client-personalization": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.24.0.tgz", @@ -128,16 +166,29 @@ "@algolia/transporter": "4.24.0" } }, - "node_modules/@algolia/client-search": { + "node_modules/@algolia/client-personalization/node_modules/@algolia/client-common": { "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", "dependencies": { - "@algolia/client-common": "4.24.0", "@algolia/requester-common": "4.24.0", "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-search": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.2.4.tgz", + "integrity": "sha512-xlBaro8nU5EvsNsLu8dSsd7jzHVvOVGCOTW4dM6gjRmQDYChzMsF69Tb1OfLaXk7YJ0jHk1rNeccBOsYBtQcIQ==", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.2.4", + "@algolia/requester-browser-xhr": "5.2.4", + "@algolia/requester-node-http": "5.2.4" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/events": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", @@ -174,7 +225,26 @@ "@algolia/transporter": "4.24.0" } }, - "node_modules/@algolia/requester-browser-xhr": { + "node_modules/@algolia/recommend/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/requester-browser-xhr": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", @@ -182,17 +252,41 @@ "@algolia/requester-common": "4.24.0" } }, + "node_modules/@algolia/recommend/node_modules/@algolia/requester-node-http": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", + "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.2.4.tgz", + "integrity": "sha512-ncssmlq86ZnoQ/RH/EEG2KgmBZQnprzx3dZZ+iJrvkbxIi8V9wBWyCgjsuPrKGitzhpnjxZLNlHJZtcps5jaXw==", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.2.4" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/requester-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==" }, "node_modules/@algolia/requester-node-http": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", - "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.2.4.tgz", + "integrity": "sha512-EoLOebO81Dtwuz/hy4onmQAb9dK8fDqyPWMwX017SvGDi3w1h4i6W6//VTO0vKLfXMNpoAKWFi+LBBTLCVtiiw==", + "peer": true, "dependencies": { - "@algolia/requester-common": "4.24.0" + "@algolia/client-common": "5.2.4" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/transporter": { @@ -2106,18 +2200,18 @@ } }, "node_modules/@docsearch/css": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", - "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==" + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.1.tgz", + "integrity": "sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==" }, "node_modules/@docsearch/react": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", - "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.1.tgz", + "integrity": "sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==", "dependencies": { "@algolia/autocomplete-core": "1.9.3", "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.0", + "@docsearch/css": "3.6.1", "algoliasearch": "^4.19.1" }, "peerDependencies": { @@ -2142,9 +2236,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", - "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.5.2.tgz", + "integrity": "sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==", "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", @@ -2156,12 +2250,12 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/cssnano-preset": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -2222,14 +2316,15 @@ "node": ">=18.0" }, "peerDependencies": { + "@mdx-js/react": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", - "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz", + "integrity": "sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==", "dependencies": { "cssnano-preset-advanced": "^6.1.2", "postcss": "^8.4.38", @@ -2241,9 +2336,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", - "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.5.2.tgz", + "integrity": "sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -2253,13 +2348,13 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", - "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz", + "integrity": "sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2294,7 +2389,6 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz", "integrity": "sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==", - "dev": true, "dependencies": { "@docusaurus/types": "3.5.2", "@types/history": "^4.7.11", @@ -2309,37 +2403,16 @@ "react-dom": "*" } }, - "node_modules/@docusaurus/module-type-aliases/node_modules/@docusaurus/types": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz", - "integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==", - "dev": true, - "dependencies": { - "@mdx-js/mdx": "^3.0.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "commander": "^5.1.0", - "joi": "^17.9.2", - "react-helmet-async": "^1.3.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1", - "webpack-merge": "^5.9.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, "node_modules/@docusaurus/plugin-client-redirects": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.4.0.tgz", - "integrity": "sha512-Pr8kyh/+OsmYCvdZhc60jy/FnrY6flD2TEAhl4rJxeVFxnvvRgEhoaIVX8q9MuJmaQoh6frPk94pjs7/6YgBDQ==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.5.2.tgz", + "integrity": "sha512-GMU0ZNoVG1DEsZlBbwLPdh0iwibrVZiRfmdppvX17SnByCVP74mb/Nne7Ss7ALgxQLtM4IHbXi8ij90VVjAJ+Q==", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "eta": "^2.2.0", "fs-extra": "^11.1.1", "lodash": "^4.17.21", @@ -2354,18 +2427,19 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", - "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "cheerio": "^1.0.0-rc.12", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz", + "integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", "lodash": "^4.17.21", @@ -2380,23 +2454,25 @@ "node": ">=18.0" }, "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", - "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz", + "integrity": "sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2414,34 +2490,16 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/plugin-content-docs/node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", - "dependencies": { - "@docusaurus/types": "3.4.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", - "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz", + "integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2455,13 +2513,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", - "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz", + "integrity": "sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2475,13 +2533,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", - "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz", + "integrity": "sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2493,13 +2551,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", - "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz", + "integrity": "sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2512,13 +2570,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", - "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz", + "integrity": "sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2530,16 +2588,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", - "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz", + "integrity": "sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2553,23 +2611,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", - "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/plugin-debug": "3.4.0", - "@docusaurus/plugin-google-analytics": "3.4.0", - "@docusaurus/plugin-google-gtag": "3.4.0", - "@docusaurus/plugin-google-tag-manager": "3.4.0", - "@docusaurus/plugin-sitemap": "3.4.0", - "@docusaurus/theme-classic": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-search-algolia": "3.4.0", - "@docusaurus/types": "3.4.0" + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz", + "integrity": "sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/plugin-debug": "3.5.2", + "@docusaurus/plugin-google-analytics": "3.5.2", + "@docusaurus/plugin-google-gtag": "3.5.2", + "@docusaurus/plugin-google-tag-manager": "3.5.2", + "@docusaurus/plugin-sitemap": "3.5.2", + "@docusaurus/theme-classic": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-search-algolia": "3.5.2", + "@docusaurus/types": "3.5.2" }, "engines": { "node": ">=18.0" @@ -2580,26 +2638,26 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", - "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz", + "integrity": "sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.43", + "infima": "0.2.0-alpha.44", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.26", @@ -2618,36 +2676,15 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/theme-classic/node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", - "dependencies": { - "@docusaurus/types": "3.4.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, "node_modules/@docusaurus/theme-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", - "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", - "dependencies": { - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.5.2.tgz", + "integrity": "sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==", + "dependencies": { + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2661,41 +2698,24 @@ "node": ">=18.0" }, "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0", "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/theme-common/node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", - "dependencies": { - "@docusaurus/types": "3.4.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", - "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz", + "integrity": "sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2714,9 +2734,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", - "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz", + "integrity": "sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -2726,9 +2746,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", - "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz", + "integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -2746,12 +2766,12 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", - "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.5.2.tgz", + "integrity": "sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2784,9 +2804,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", - "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.5.2.tgz", + "integrity": "sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==", "dependencies": { "tslib": "^2.6.0" }, @@ -2803,13 +2823,13 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", - "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz", + "integrity": "sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -3975,9 +3995,9 @@ } }, "node_modules/algoliasearch-helper": { - "version": "3.22.2", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.2.tgz", - "integrity": "sha512-3YQ6eo7uYOCHeQ2ZpD+OoT3aJJwMNKEnwtu8WMzm81XmBOSCwRjQditH9CeSOQ38qhHkuGw23pbq+kULkIJLcw==", + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.4.tgz", + "integrity": "sha512-fvBCywguW9f+939S6awvRMstqMF1XXcd2qs1r1aGqL/PJ1go/DqN06tWmDVmhCDqBJanm++imletrQWf0G2S1g==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -3985,6 +4005,41 @@ "algoliasearch": ">= 3.1 < 6" } }, + "node_modules/algoliasearch/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-browser-xhr": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", + "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-node-http": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", + "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -4096,9 +4151,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "funding": [ { "type": "opencollective", @@ -4114,11 +4169,11 @@ } ], "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -4337,9 +4392,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "funding": [ { "type": "opencollective", @@ -4355,10 +4410,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -4463,9 +4518,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001640", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz", - "integrity": "sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==", + "version": "1.0.30001655", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", + "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", "funding": [ { "type": "opencollective", @@ -5735,9 +5790,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.819", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.819.tgz", - "integrity": "sha512-8RwI6gKUokbHWcN3iRij/qpvf/wCbIVY5slODi85werwqUQwpFXM+dvUBND93Qh7SB0pW3Hlq3/wZsqQ3M9Jaw==" + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -7526,9 +7581,9 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.43", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", - "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", + "version": "0.2.0-alpha.44", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.44.tgz", + "integrity": "sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==", "engines": { "node": ">=12" } @@ -10480,9 +10535,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -11972,9 +12027,9 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-json-view-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", - "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz", + "integrity": "sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==", "engines": { "node": ">=14" }, @@ -12539,9 +12594,9 @@ "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" }, "node_modules/rtlcss": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz", - "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0", @@ -12633,9 +12688,9 @@ } }, "node_modules/search-insights": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", - "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.1.tgz", + "integrity": "sha512-HHFjYH/0AqXacETlIbe9EYc3UNlQYGNNTY0fZ/sWl6SweX+GDxq9NB5+RVoPLgEFuOtCz7M9dhYxqDnhbbF0eQ==", "peer": true }, "node_modules/section-matter": { diff --git a/docs/package.json b/docs/package.json index f8729aa15..c295a8abf 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,9 +14,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-client-redirects": "^3.4.0", - "@docusaurus/preset-classic": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/plugin-client-redirects": "^3.5.2", + "@docusaurus/preset-classic": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -26,7 +26,7 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "3.5.2", - "@docusaurus/types": "3.4.0" + "@docusaurus/types": "3.5.2" }, "browserslist": { "production": [ From bf64edf34396c115cf15b4d17b990205e83e9c6c Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 5 Sep 2024 13:30:38 +0900 Subject: [PATCH 018/282] Apply black code formatter --- slack_bolt/__init__.py | 1 + slack_bolt/adapter/socket_mode/async_handler.py | 1 + slack_bolt/async_app.py | 1 + slack_bolt/authorization/__init__.py | 1 + slack_bolt/context/async_context.py | 8 ++------ slack_bolt/context/context.py | 8 ++------ slack_bolt/error/__init__.py | 1 + slack_bolt/kwargs_injection/args.py | 2 +- slack_bolt/kwargs_injection/async_args.py | 2 +- slack_bolt/lazy_listener/__init__.py | 1 + slack_bolt/listener_matcher/__init__.py | 1 + slack_bolt/request/__init__.py | 1 + tests/scenario_tests_async/test_events_org_apps.py | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/slack_bolt/__init__.py b/slack_bolt/__init__.py index d9df66085..789b93c92 100644 --- a/slack_bolt/__init__.py +++ b/slack_bolt/__init__.py @@ -5,6 +5,7 @@ * GitHub repository: https://github.com/slackapi/bolt-python * The class representing a Bolt app: `slack_bolt.app.app` """ # noqa: E501 + # Don't add async module imports here from .app import App from .context import BoltContext diff --git a/slack_bolt/adapter/socket_mode/async_handler.py b/slack_bolt/adapter/socket_mode/async_handler.py index 0044b0e9c..09e3ea433 100644 --- a/slack_bolt/adapter/socket_mode/async_handler.py +++ b/slack_bolt/adapter/socket_mode/async_handler.py @@ -1,4 +1,5 @@ """Default implementation is the aiohttp-based one.""" + from .aiohttp import AsyncSocketModeHandler __all__ = [ diff --git a/slack_bolt/async_app.py b/slack_bolt/async_app.py index 9fdb5a794..3157e8006 100644 --- a/slack_bolt/async_app.py +++ b/slack_bolt/async_app.py @@ -44,6 +44,7 @@ async def command(ack, body, respond): Refer to `slack_bolt.app.async_app` for more details. """ # noqa: E501 + from .app.async_app import AsyncApp from .context.ack.async_ack import AsyncAck from .context.async_context import AsyncBoltContext diff --git a/slack_bolt/authorization/__init__.py b/slack_bolt/authorization/__init__.py index efd9262a8..a936a866b 100644 --- a/slack_bolt/authorization/__init__.py +++ b/slack_bolt/authorization/__init__.py @@ -3,6 +3,7 @@ Refer to https://slack.dev/bolt-python/concepts#authorization for details. """ + from .authorize_result import AuthorizeResult __all__ = [ diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index 49748366c..58eba0850 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -155,9 +155,7 @@ async def handle_button_clicks(context): Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete( - client=self.client, function_execution_id=self.function_execution_id - ) + self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property @@ -181,7 +179,5 @@ async def handle_button_clicks(context): Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail( - client=self.client, function_execution_id=self.function_execution_id - ) + self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index 7d20e3a07..c9194abb8 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -156,9 +156,7 @@ def handle_button_clicks(context): Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id - ) + self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property @@ -182,7 +180,5 @@ def handle_button_clicks(context): Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id - ) + self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] diff --git a/slack_bolt/error/__init__.py b/slack_bolt/error/__init__.py index 5b866e2d5..19716cd74 100644 --- a/slack_bolt/error/__init__.py +++ b/slack_bolt/error/__init__.py @@ -1,4 +1,5 @@ """Bolt specific error types.""" + from typing import Optional, Union diff --git a/slack_bolt/kwargs_injection/args.py b/slack_bolt/kwargs_injection/args.py index 2a1d2c72b..68e64a8e8 100644 --- a/slack_bolt/kwargs_injection/args.py +++ b/slack_bolt/kwargs_injection/args.py @@ -119,7 +119,7 @@ def __init__( # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], None], - **kwargs # noqa + **kwargs, # noqa ): self.logger: logging.Logger = logger self.client: WebClient = client diff --git a/slack_bolt/kwargs_injection/async_args.py b/slack_bolt/kwargs_injection/async_args.py index 879c4a031..1601a552a 100644 --- a/slack_bolt/kwargs_injection/async_args.py +++ b/slack_bolt/kwargs_injection/async_args.py @@ -115,7 +115,7 @@ def __init__( complete: AsyncComplete, fail: AsyncFail, next: Callable[[], Awaitable[None]], - **kwargs # noqa + **kwargs, # noqa ): self.logger: Logger = logger self.client: AsyncWebClient = client diff --git a/slack_bolt/lazy_listener/__init__.py b/slack_bolt/lazy_listener/__init__.py index 0a8e7c0b4..4d9111cc3 100644 --- a/slack_bolt/lazy_listener/__init__.py +++ b/slack_bolt/lazy_listener/__init__.py @@ -21,6 +21,7 @@ def run_long_process(respond, body): Refer to https://slack.dev/bolt-python/concepts#lazy-listeners for more details. """ + # Don't add async module imports here from .runner import LazyListenerRunner from .thread_runner import ThreadLazyListenerRunner diff --git a/slack_bolt/listener_matcher/__init__.py b/slack_bolt/listener_matcher/__init__.py index 352c35c48..26f164ba6 100644 --- a/slack_bolt/listener_matcher/__init__.py +++ b/slack_bolt/listener_matcher/__init__.py @@ -2,6 +2,7 @@ A listener matcher function returns bool value instead of `next()` method invocation inside. This interface enables developers to utilize simple predicate functions for additional listener conditions. """ + # Don't add async module imports here from .custom_listener_matcher import CustomListenerMatcher from .listener_matcher import ListenerMatcher diff --git a/slack_bolt/request/__init__.py b/slack_bolt/request/__init__.py index 0a0620611..ee8b435a7 100644 --- a/slack_bolt/request/__init__.py +++ b/slack_bolt/request/__init__.py @@ -3,6 +3,7 @@ Refer to https://api.slack.com/apis/connections for the two types of connections. This interface encapsulates the difference between the two. """ + # Don't add async module imports here from .request import BoltRequest diff --git a/tests/scenario_tests_async/test_events_org_apps.py b/tests/scenario_tests_async/test_events_org_apps.py index 5e7321565..187c59b77 100644 --- a/tests/scenario_tests_async/test_events_org_apps.py +++ b/tests/scenario_tests_async/test_events_org_apps.py @@ -32,7 +32,7 @@ async def async_find_installation( enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, - is_enterprise_install: Optional[bool] = False + is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: assert enterprise_id == "E111" assert team_id is None From 76337259bc29e2be8e80a43f6477d892da7934cf Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Mon, 16 Sep 2024 09:37:25 -0700 Subject: [PATCH 019/282] docs: changes URL (#1160) --- README.md | 10 +++++----- docs/README.md | 2 +- docs/content/advanced/global-middleware.md | 2 +- docs/content/advanced/listener-middleware.md | 2 +- docs/content/basic/acknowledge.md | 2 +- docs/content/basic/action-listening.md | 2 +- docs/content/basic/action-respond.md | 2 +- docs/content/basic/app-home.md | 2 +- docs/content/basic/authenticating-oauth.md | 2 +- docs/content/basic/commands.md | 2 +- docs/content/basic/custom-steps.md | 2 +- docs/content/basic/event-listening.md | 2 +- docs/content/basic/message-listening.md | 2 +- docs/content/basic/message-sending.md | 2 +- docs/content/basic/opening-modals.md | 2 +- docs/content/basic/options.md | 4 ++-- docs/content/basic/shortcuts.md | 2 +- docs/content/basic/updating-pushing-views.md | 2 +- docs/content/basic/view_submissions.md | 2 +- docs/content/basic/web-api.md | 4 ++-- docs/content/getting-started.md | 2 +- docs/content/steps/adding-editing-steps.md | 2 +- docs/content/steps/creating-steps.md | 4 ++-- docs/content/steps/executing-steps.md | 2 +- docs/content/steps/saving-steps.md | 2 +- docs/content/tutorial/getting-started-http.md | 2 +- docs/docusaurus.config.js | 18 +++++++++--------- docs/i18n/ja-jp/README.md | 2 +- .../current/advanced/global-middleware.md | 2 +- .../current/advanced/listener-middleware.md | 2 +- .../current/basic/acknowledge.md | 2 +- .../current/basic/action-listening.md | 2 +- .../current/basic/action-respond.md | 2 +- .../current/basic/app-home.md | 2 +- .../current/basic/authenticating-oauth.md | 2 +- .../current/basic/commands.md | 2 +- .../current/basic/custom-steps.md | 2 +- .../current/basic/event-listening.md | 2 +- .../current/basic/message-listening.md | 2 +- .../current/basic/message-sending.md | 2 +- .../current/basic/opening-modals.md | 2 +- .../current/basic/options.md | 4 ++-- .../current/basic/shortcuts.md | 2 +- .../current/basic/updating-pushing-views.md | 2 +- .../current/basic/view_submissions.md | 2 +- .../current/basic/web-api.md | 4 ++-- .../current/getting-started.md | 2 +- .../current/steps/adding-editing-steps.md | 2 +- .../current/steps/creating-steps.md | 4 ++-- .../current/steps/executing-steps.md | 2 +- .../current/steps/saving-steps.md | 2 +- .../current/tutorial/getting-started-http.md | 2 +- docs/sidebars.js | 2 +- 53 files changed, 71 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 30a62198c..7576597d5 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@
    Python Versions - + Documentation

    -A Python framework to build Slack apps in a flash with the latest platform features. Read the [getting started guide](https://slack.dev/bolt-python/getting-started) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. The Python module documents are available [here](https://slack.dev/bolt-python/api-docs/slack_bolt/). +A Python framework to build Slack apps in a flash with the latest platform features. Read the [getting started guide](https://tools.slack.dev/bolt-python/getting-started) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. The Python module documents are available [here](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/). ## Setup @@ -140,10 +140,10 @@ Most of the app's functionality will be inside listener functions (the `fn` para | `body` | Dictionary that contains the entire body of the request (superset of `payload`). Some accessory data is only available outside of the payload (such as `trigger_id` and `authorizations`). | `payload` | Contents of the incoming event. The payload structure depends on the listener. For example, for an Events API event, `payload` will be the [event type structure](https://api.slack.com/events-api#event_type_structure). For a block action, it will be the action from within the `actions` list. The `payload` dictionary is also accessible via the alias corresponding to the listener (`message`, `event`, `action`, `shortcut`, `view`, `command`, or `options`). For example, if you were building a `message()` listener, you could use the `payload` and `message` arguments interchangably. **An easy way to understand what's in a payload is to log it**. | | `context` | Event context. This dictionary contains data about the event and app, such as the `botId`. Middleware can add additional context before the event is passed to listeners. -| `ack` | Function that **must** be called to acknowledge that your app received the incoming event. `ack` exists for all actions, shortcuts, view submissions, slash command and options requests. `ack` returns a promise that resolves when complete. Read more in [Acknowledging events](https://slack.dev/bolt-python/concepts/acknowledge). +| `ack` | Function that **must** be called to acknowledge that your app received the incoming event. `ack` exists for all actions, shortcuts, view submissions, slash command and options requests. `ack` returns a promise that resolves when complete. Read more in [Acknowledging events](https://tools.slack.dev/bolt-python/concepts/acknowledge). | `respond` | Utility function that responds to incoming events **if** it contains a `response_url` (shortcuts, actions, and slash commands). | `say` | Utility function to send a message to the channel associated with the incoming event. This argument is only available when the listener is triggered for events that contain a `channel_id` (the most common being `message` events). `say` accepts simple strings (for plain-text messages) and dictionaries (for messages containing blocks). -| `client` | Web API client that uses the token associated with the event. For single-workspace installations, the token is provided to the constructor. For multi-workspace installations, the token is returned by using [the OAuth library](https://slack.dev/bolt-python/concepts/authenticating-oauth), or manually using the `authorize` function. +| `client` | Web API client that uses the token associated with the event. For single-workspace installations, the token is provided to the constructor. For multi-workspace installations, the token is returned by using [the OAuth library](https://tools.slack.dev/bolt-python/concepts/authenticating-oauth), or manually using the `authorize` function. | `logger` | The built-in [`logging.Logger`](https://docs.python.org/3/library/logging.html) instance you can use in middleware/listeners. | `complete` | Utility function used to signal the successful completion of a custom step execution. This tells Slack to proceed with the next steps in the workflow. This argument is only available with the `.function` and `.action` listener when handling custom workflow step executions. | `fail` | Utility function used to signal that a custom step failed to complete. This tells Slack to stop the workflow execution. This argument is only available with the `.function` and `.action` listener when handling custom workflow step executions. @@ -192,7 +192,7 @@ Apps can be run the same way as the syncronous example above. If you'd prefer an ## Getting Help -[The documentation](https://slack.dev/bolt-python) has more information on basic and advanced concepts for Bolt for Python. Also, all the Python module documents of this library are available [here](https://slack.dev/bolt-python/api-docs/slack_bolt/). +[The documentation](https://tools.slack.dev/bolt-python) has more information on basic and advanced concepts for Bolt for Python. Also, all the Python module documents of this library are available [here](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/). If you otherwise get stuck, we're here to help. The following are the best ways to get assistance working through your issue: diff --git a/docs/README.md b/docs/README.md index e88af2c1d..22a279f04 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -# slack.dev/bolt-python +# tools.slack.dev/bolt-python This website is built using [Docusaurus](https://docusaurus.io/). 'Tis cool. diff --git a/docs/content/advanced/global-middleware.md b/docs/content/advanced/global-middleware.md index f74f447f1..61aa97066 100644 --- a/docs/content/advanced/global-middleware.md +++ b/docs/content/advanced/global-middleware.md @@ -11,7 +11,7 @@ Both global and listener middleware must call `next()` to pass control of the ex -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python @app.use def auth_acme(client, context, logger, payload, next): diff --git a/docs/content/advanced/listener-middleware.md b/docs/content/advanced/listener-middleware.md index de4d4c7e1..338cb0d4f 100644 --- a/docs/content/advanced/listener-middleware.md +++ b/docs/content/advanced/listener-middleware.md @@ -8,7 +8,7 @@ Listener middleware is only run for the listener in which it's passed. You can p If your listener middleware is a quite simple one, you can use a listener matcher, which returns `bool` value (`True` for proceeding) instead of requiring `next()` method call. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Listener middleware which filters out messages with "bot_message" subtype diff --git a/docs/content/basic/acknowledge.md b/docs/content/basic/acknowledge.md index d880eebb1..b1d2000ef 100644 --- a/docs/content/basic/acknowledge.md +++ b/docs/content/basic/acknowledge.md @@ -16,7 +16,7 @@ When working in a FaaS / serverless environment, our guidelines for when to `ack ::: -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Example of responding to an external_select options request @app.options("menu_selection") diff --git a/docs/content/basic/action-listening.md b/docs/content/basic/action-listening.md index a7b3676e9..cd677d22d 100644 --- a/docs/content/basic/action-listening.md +++ b/docs/content/basic/action-listening.md @@ -10,7 +10,7 @@ Actions can be filtered on an `action_id` of type `str` or `re.Pattern`. `action You'll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests section](/concepts/acknowledge). -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Your listener will be called every time a block element with the action_id "approve_button" is triggered @app.action("approve_button") diff --git a/docs/content/basic/action-respond.md b/docs/content/basic/action-respond.md index 95ceef8ad..7153f18bd 100644 --- a/docs/content/basic/action-respond.md +++ b/docs/content/basic/action-respond.md @@ -8,7 +8,7 @@ There are two main ways to respond to actions. The first (and most common) way i The second way to respond to actions is using `respond()`, which is a utility to use the `response_url` associated with the action. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Your listener will be called every time an interactive component with the action_id “approve_button” is triggered @app.action("approve_button") diff --git a/docs/content/basic/app-home.md b/docs/content/basic/app-home.md index c76f91f56..a7a6a1f02 100644 --- a/docs/content/basic/app-home.md +++ b/docs/content/basic/app-home.md @@ -8,7 +8,7 @@ slug: /concepts/app-home You can subscribe to the `app_home_opened` event to listen for when users open your App Home. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python @app.event("app_home_opened") def update_home_tab(client, event, logger): diff --git a/docs/content/basic/authenticating-oauth.md b/docs/content/basic/authenticating-oauth.md index 19bca0069..321803497 100644 --- a/docs/content/basic/authenticating-oauth.md +++ b/docs/content/basic/authenticating-oauth.md @@ -4,7 +4,7 @@ lang: en slug: /concepts/authenticating-oauth --- -Slack apps installed on multiple workspaces will need to implement OAuth, then store installation information (like access tokens) securely. By providing `client_id`, `client_secret`, `scopes`, `installation_store`, and `state_store` when initializing App, Bolt for Python will handle the work of setting up OAuth routes and verifying state. If you're implementing a custom adapter, you can make use of our [OAuth library](https://slack.dev/python-slack-sdk/oauth/), which is what Bolt for Python uses under the hood. +Slack apps installed on multiple workspaces will need to implement OAuth, then store installation information (like access tokens) securely. By providing `client_id`, `client_secret`, `scopes`, `installation_store`, and `state_store` when initializing App, Bolt for Python will handle the work of setting up OAuth routes and verifying state. If you're implementing a custom adapter, you can make use of our [OAuth library](https://tools.slack.dev/python-slack-sdk/oauth/), which is what Bolt for Python uses under the hood. Bolt for Python will create a **Redirect URL** `slack/oauth_redirect`, which Slack uses to redirect users after they complete your app's installation flow. You will need to add this **Redirect URL** in your app configuration settings under **OAuth and Permissions**. This path can be configured in the `OAuthSettings` argument described below. diff --git a/docs/content/basic/commands.md b/docs/content/basic/commands.md index 2bba37285..010ef32d2 100644 --- a/docs/content/basic/commands.md +++ b/docs/content/basic/commands.md @@ -12,7 +12,7 @@ There are two ways to respond to slash commands. The first way is to use `say()` When setting up commands within your app configuration, you'll append `/slack/events` to your request URL. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # The echo command simply echoes on command @app.command("/echo") diff --git a/docs/content/basic/custom-steps.md b/docs/content/basic/custom-steps.md index 17af8234b..1eedf9f5d 100644 --- a/docs/content/basic/custom-steps.md +++ b/docs/content/basic/custom-steps.md @@ -11,7 +11,7 @@ Your app can use the `function()` method to listen to incoming [custom step requ You can reference your custom step's inputs using the `inputs` listener argument of type `dict`. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn about the available listener arguments. +Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn about the available listener arguments. ```python # This sample custom step formats an input and outputs it diff --git a/docs/content/basic/event-listening.md b/docs/content/basic/event-listening.md index f4f4406f6..95c6e84ea 100644 --- a/docs/content/basic/event-listening.md +++ b/docs/content/basic/event-listening.md @@ -8,7 +8,7 @@ You can listen to [any Events API event](https://api.slack.com/events) using the The `event()` method requires an `eventType` of type `str`. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # When a user joins the workspace, send a message in a predefined channel asking them to introduce themselves @app.event("team_join") diff --git a/docs/content/basic/message-listening.md b/docs/content/basic/message-listening.md index a1b59278f..0243b1537 100644 --- a/docs/content/basic/message-listening.md +++ b/docs/content/basic/message-listening.md @@ -10,7 +10,7 @@ To listen to messages that [your app has access to receive](https://api.slack.co :::info -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ::: diff --git a/docs/content/basic/message-sending.md b/docs/content/basic/message-sending.md index 20af00f07..d7b5d2da9 100644 --- a/docs/content/basic/message-sending.md +++ b/docs/content/basic/message-sending.md @@ -8,7 +8,7 @@ Within your listener function, `say()` is available whenever there is an associa In the case that you'd like to send a message outside of a listener or you want to do something more advanced (like handle specific errors), you can call `client.chat_postMessage` [using the client attached to your Bolt instance](/concepts/web-api). -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Listens for messages containing "knock knock" and responds with an italicized "who's there?" @app.message("knock knock") diff --git a/docs/content/basic/opening-modals.md b/docs/content/basic/opening-modals.md index 6efa9c571..1e231bcc1 100644 --- a/docs/content/basic/opening-modals.md +++ b/docs/content/basic/opening-modals.md @@ -10,7 +10,7 @@ Your app receives `trigger_id`s in payloads sent to your Request URL that are tr Read more about modal composition in the [API documentation](https://api.slack.com/surfaces/modals/using#composing_views). -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Listen for a shortcut invocation diff --git a/docs/content/basic/options.md b/docs/content/basic/options.md index 791ba3daa..e7c1e1243 100644 --- a/docs/content/basic/options.md +++ b/docs/content/basic/options.md @@ -11,9 +11,9 @@ While it's recommended to use `action_id` for `external_select` menus, dialogs d To respond to options requests, you'll need to call `ack()` with a valid `options` or `option_groups` list. Both [external select response examples](https://api.slack.com/reference/messaging/block-elements#external-select) and [dialog response examples](https://api.slack.com/dialogs#dynamic_select_elements_external) can be found on our API site. -Additionally, you may want to apply filtering logic to the returned options based on user input. This can be accomplished by using the `payload` argument to your options listener and checking for the contents of the `value` property within it. Based on the `value` you can return different options. All listeners and middleware handlers in Bolt for Python have access to [many useful arguments](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) - be sure to check them out! +Additionally, you may want to apply filtering logic to the returned options based on user input. This can be accomplished by using the `payload` argument to your options listener and checking for the contents of the `value` property within it. Based on the `value` you can return different options. All listeners and middleware handlers in Bolt for Python have access to [many useful arguments](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) - be sure to check them out! -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Example of responding to an external_select options request @app.options("external_action") diff --git a/docs/content/basic/shortcuts.md b/docs/content/basic/shortcuts.md index 28e1d24f7..6f469c20f 100644 --- a/docs/content/basic/shortcuts.md +++ b/docs/content/basic/shortcuts.md @@ -16,7 +16,7 @@ When setting up shortcuts within your app configuration, as with other URLs, you ⚠️ Note that global shortcuts do **not** include a channel ID. If your app needs access to a channel ID, you may use a [`conversations_select`](https://api.slack.com/reference/block-kit/block-elements#conversation_select) element within a modal. Message shortcuts do include a channel ID. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # The open_modal shortcut listens to a shortcut with the callback_id "open_modal" @app.shortcut("open_modal") diff --git a/docs/content/basic/updating-pushing-views.md b/docs/content/basic/updating-pushing-views.md index c25e8fe98..8cc45d49a 100644 --- a/docs/content/basic/updating-pushing-views.md +++ b/docs/content/basic/updating-pushing-views.md @@ -16,7 +16,7 @@ To push a new view onto the view stack, you can use the built-in client to call Learn more about updating and pushing views in our API documentation. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Listen for a button invocation with action_id `button_abc` (assume it's inside of a modal) @app.action("button_abc") diff --git a/docs/content/basic/view_submissions.md b/docs/content/basic/view_submissions.md index d62b93a26..b1c3e7cef 100644 --- a/docs/content/basic/view_submissions.md +++ b/docs/content/basic/view_submissions.md @@ -66,7 +66,7 @@ def handle_view_closed(ack, body, logger): -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ```python # Handle a view_submission request @app.view("view_1") diff --git a/docs/content/basic/web-api.md b/docs/content/basic/web-api.md index 07ee78f70..cf30c29be 100644 --- a/docs/content/basic/web-api.md +++ b/docs/content/basic/web-api.md @@ -4,13 +4,13 @@ lang: en slug: /concepts/web-api --- -You can call [any Web API method](https://api.slack.com/methods) using the [`WebClient`](https://slack.dev/python-slack-sdk/basic_usage.html) 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. +You can call [any Web API method](https://api.slack.com/methods) using the [`WebClient`](https://tools.slack.dev/python-slack-sdk/basic_usage.html) 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. The token used to initialize Bolt can be found in the `context` object, which is required to call most Web API methods. :::info -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. +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. ::: diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index 007e28ab8..79ccfff61 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -184,7 +184,7 @@ app = App(token=os.environ.get("SLACK_BOT_TOKEN")) # Listens to incoming messages that contain "hello" # To learn available listener arguments, -# visit https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered diff --git a/docs/content/steps/adding-editing-steps.md b/docs/content/steps/adding-editing-steps.md index c10d1ea8d..99ca6d587 100644 --- a/docs/content/steps/adding-editing-steps.md +++ b/docs/content/steps/adding-editing-steps.md @@ -22,7 +22,7 @@ Within the `edit` callback, the `configure()` utility can be used to easily open To learn more about opening configuration modals, [read the documentation](https://api.slack.com/workflows/steps#handle_config_view). -Refer to the module documents (common / step-specific) to learn the available arguments. +Refer to the module documents (common / step-specific) to learn the available arguments. ```python def edit(ack, step, configure): diff --git a/docs/content/steps/creating-steps.md b/docs/content/steps/creating-steps.md index e585d1440..6728a77e1 100644 --- a/docs/content/steps/creating-steps.md +++ b/docs/content/steps/creating-steps.md @@ -22,9 +22,9 @@ The configuration object contains three keys: `edit`, `save`, and `execute`. Eac After instantiating a `WorkflowStep`, you can pass it into `app.step()`. Behind the scenes, your app will listen and respond to the step’s events using the callbacks provided in the configuration object. -Alternatively, steps from apps can also be created using the `WorkflowStepBuilder` class alongside a decorator pattern. For more information, including an example of this approach, [refer to the documentation](https://slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder). +Alternatively, steps from apps can also be created using the `WorkflowStepBuilder` class alongside a decorator pattern. For more information, including an example of this approach, [refer to the documentation](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder). -Refer to the module documents (common / step-specific) to learn the available arguments. +Refer to the module documents (common / step-specific) to learn the available arguments. ```python import os diff --git a/docs/content/steps/executing-steps.md b/docs/content/steps/executing-steps.md index fa5bb64c9..12d557cd0 100644 --- a/docs/content/steps/executing-steps.md +++ b/docs/content/steps/executing-steps.md @@ -20,7 +20,7 @@ Using the `inputs` from the `save` callback, this is where you can make third-pa Within the `execute` callback, your app must either call `complete()` to indicate that the step's execution was successful, or `fail()` to indicate that the step's execution failed. -Refer to the module documents (common / step-specific) to learn the available arguments. +Refer to the module documents (common / step-specific) to learn the available arguments. ```python def execute(step, complete, fail): inputs = step["inputs"] diff --git a/docs/content/steps/saving-steps.md b/docs/content/steps/saving-steps.md index 931e973ed..079cf5d71 100644 --- a/docs/content/steps/saving-steps.md +++ b/docs/content/steps/saving-steps.md @@ -25,7 +25,7 @@ Within the `save` callback, the `update()` method can be used to save the builde To learn more about how to structure these parameters, [read the documentation](https://api.slack.com/reference/workflows/workflow_step). -Refer to the module documents (common / step-specific) to learn the available arguments. +Refer to the module documents (common / step-specific) to learn the available arguments. ```python def save(ack, view, update): ack() diff --git a/docs/content/tutorial/getting-started-http.md b/docs/content/tutorial/getting-started-http.md index 2ac4b1401..6cd4a8ae6 100644 --- a/docs/content/tutorial/getting-started-http.md +++ b/docs/content/tutorial/getting-started-http.md @@ -177,7 +177,7 @@ app = App( # Listens to incoming messages that contain "hello" # To learn available listener arguments, -# visit https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 4a2556458..51b9e06c7 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -12,7 +12,7 @@ const config = { tagline: "Official frameworks, libraries, and SDKs for Slack developers", favicon: "img/favicon.ico", - url: "https://slack.dev", + url: "https://tools.slack.dev", baseUrl: "/bolt-python/", organizationName: "slackapi", projectName: "bolt-python", @@ -84,7 +84,7 @@ const config = { logo: { alt: "Slack logo", src: "img/slack-logo.svg", - href: "https://slack.dev", + href: "https://tools.slack.dev", target: "_self", }, items: [ @@ -95,17 +95,17 @@ const config = { items: [ { label: "Java", - to: "https://slack.dev/java-slack-sdk/guides/bolt-basics", + to: "https://tools.slack.dev/java-slack-sdk/guides/bolt-basics", target: "_self", }, { label: "JavaScript", - to: "https://slack.dev/bolt-js", + to: "https://tools.slack.dev/bolt-js", target: "_self", }, { label: "Python", - to: "https://slack.dev/bolt-python", + to: "https://tools.slack.dev/bolt-python", target: "_self", }, ], @@ -117,17 +117,17 @@ const config = { items: [ { label: "Java Slack SDK", - to: "https://slack.dev/java-slack-sdk/", + to: "https://tools.slack.dev/java-slack-sdk/", target: "_self", }, { label: "Node Slack SDK", - to: "https://slack.dev/node-slack-sdk/", + to: "https://tools.slack.dev/node-slack-sdk/", target: "_self", }, { label: "Python Slack SDK", - to: "https://slack.dev/python-slack-sdk/", + to: "https://tools.slack.dev/python-slack-sdk/", target: "_self", }, { @@ -144,7 +144,7 @@ const config = { items: [ { label: "Community tools", - to: "https://slack.dev/community-tools", + to: "https://tools.slack.dev/community-tools", target: "_self", }, { diff --git a/docs/i18n/ja-jp/README.md b/docs/i18n/ja-jp/README.md index d9cb5dd72..e23cb969b 100644 --- a/docs/i18n/ja-jp/README.md +++ b/docs/i18n/ja-jp/README.md @@ -118,4 +118,4 @@ For example: }, ``` -Be careful changing `code.json`. If you change something in this repo, it will likely need to be changed in the other Slack.dev repos too, like the Bolt-Python repo. We want these translations to match for all Slack.dev sites. \ No newline at end of file +Be careful changing `code.json`. If you change something in this repo, it will likely need to be changed in the other tools.slack.dev repos too, like the Bolt-Python repo. We want these translations to match for all tools.slack.dev sites. \ No newline at end of file diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/global-middleware.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/global-middleware.md index a9ee8264f..caace0621 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/global-middleware.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/global-middleware.md @@ -9,7 +9,7 @@ order: 8 グローバルミドルウェアでもリスナーミドルウェアでも、次のミドルウェアに実行チェーンの制御をリレーするために、`next()` を呼び出す必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python @app.use diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/listener-middleware.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/listener-middleware.md index 83ecf4b5c..822b5ac63 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/listener-middleware.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/listener-middleware.md @@ -8,7 +8,7 @@ slug: /concepts/listener-middleware 非常にシンプルなリスナーミドルウェアの場合であれば、`next()` メソッドを呼び出す代わりに `bool` 値(処理を継続したい場合は `True`)を返すだけで済む「リスナーマッチャー」を使うとよいでしょう。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # "bot_message" サブタイプのメッセージを抽出するリスナーミドルウェア diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/acknowledge.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/acknowledge.md index 6e54b86ca..d180a966d 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/acknowledge.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/acknowledge.md @@ -16,7 +16,7 @@ slug: /concepts/acknowledge -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 外部データを使用する選択メニューオプションに応答するサンプル @app.options("menu_selection") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-listening.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-listening.md index ed8e4b256..7be3340d6 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-listening.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-listening.md @@ -10,7 +10,7 @@ Bolt アプリは `action` メソッドを用いて、ボタンのクリック `action()` を使ったすべての例で `ack()` が使用されていることに注目してください。アクションのリスナー内では、Slack からのリクエストを受信したことを確認するために、`ack()` 関数を呼び出す必要があります。これについては、[リクエストの確認](/concepts/acknowledge)セクションで説明しています。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'approve_button' という action_id のブロックエレメントがトリガーされるたびに、このリスナーが呼び出させれる @app.action("approve_button") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-respond.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-respond.md index fbcc5027e..3a31a05c5 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-respond.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/action-respond.md @@ -8,7 +8,7 @@ slug: /concepts/action-respond 2 つ目は、`respond()` を使用する方法です。これは、アクションに関連づけられた `response_url` を使ったメッセージ送信を行うためのユーティリティです。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'approve_button' という action_id のインタラクティブコンポーネントがトリガーされると、このリスナーが呼ばれる @app.action("approve_button") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/app-home.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/app-home.md index 1de6b5259..6954bb75e 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/app-home.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/app-home.md @@ -8,7 +8,7 @@ slug: /concepts/app-home `app_home_opened` イベントをサブスクライブすると、ユーザーが App Home を開く操作をリッスンできます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python @app.event("app_home_opened") def update_home_tab(client, event, logger): diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/authenticating-oauth.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/authenticating-oauth.md index 78442bd30..b1478ee3b 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/authenticating-oauth.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/authenticating-oauth.md @@ -4,7 +4,7 @@ lang: ja-jp slug: /concepts/authenticating-oauth --- -Slack アプリを複数のワークスペースにインストールできるようにするためには、OAuth フローを実装した上で、アクセストークンなどのインストールに関する情報をセキュアな方法で保存する必要があります。アプリを初期化する際に `client_id`、`client_secret`、`scopes`、`installation_store`、`state_store` を指定することで、OAuth のエンドポイントのルート情報や stateパラメーターの検証をBolt for Python にハンドリングさせることができます。カスタムのアダプターを実装する場合は、SDK が提供する組み込みの[OAuth ライブラリ](https://slack.dev/python-slack-sdk/oauth/)を利用するのが便利です。これは Slack が開発したモジュールで、Bolt for Python 内部でも利用しています。 +Slack アプリを複数のワークスペースにインストールできるようにするためには、OAuth フローを実装した上で、アクセストークンなどのインストールに関する情報をセキュアな方法で保存する必要があります。アプリを初期化する際に `client_id`、`client_secret`、`scopes`、`installation_store`、`state_store` を指定することで、OAuth のエンドポイントのルート情報や stateパラメーターの検証をBolt for Python にハンドリングさせることができます。カスタムのアダプターを実装する場合は、SDK が提供する組み込みの[OAuth ライブラリ](https://tools.slack.dev/python-slack-sdk/oauth/)を利用するのが便利です。これは Slack が開発したモジュールで、Bolt for Python 内部でも利用しています。 Bolt for Python によって `slack/oauth_redirect` という**リダイレクト URL** が生成されます。Slack はアプリのインストールフローを完了させたユーザーをこの URL にリダイレクトします。この**リダイレクト URL** は、アプリの設定の「**OAuth and Permissions**」であらかじめ追加しておく必要があります。この URL は、後ほど説明するように `OAuthSettings` というコンストラクタの引数で指定することもできます。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/commands.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/commands.md index ced4f5629..73d262446 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/commands.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/commands.md @@ -12,7 +12,7 @@ slug: /concepts/commands アプリの設定でコマンドを登録するときは、リクエスト URL の末尾に `/slack/events` をつけます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # echoコマンドは受け取ったコマンドをそのまま返す @app.command("/echo") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/custom-steps.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/custom-steps.md index 0f272a09d..bc1089d21 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/custom-steps.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/custom-steps.md @@ -11,7 +11,7 @@ Your app can use the `function()` method to listen to incoming [custom step requ You can reference your custom step's inputs using the `inputs` listener argument of type `dict`. -Refer to [the module document](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn about the available listener arguments. +Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn about the available listener arguments. ```python # This sample custom step formats an input and outputs it diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/event-listening.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/event-listening.md index 12fac105e..6d0409bb0 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/event-listening.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/event-listening.md @@ -8,7 +8,7 @@ slug: /concepts/event-listening `event()` メソッドには `str` 型の `eventType` を指定する必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # ユーザーがワークスペースに参加した際に、自己紹介を促すメッセージを指定のチャンネルに送信 @app.event("team_join") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-listening.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-listening.md index 8a21b425c..a30620abd 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-listening.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-listening.md @@ -8,7 +8,7 @@ slug: /concepts/message-listening `message()` の引数には `str` 型または `re.Pattern` オブジェクトを指定できます。この条件のパターンに一致しないメッセージは除外されます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # '👋' が含まれるすべてのメッセージに一致 @app.message(":wave:") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-sending.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-sending.md index 406b16ca0..8b5c9e7e5 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-sending.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/message-sending.md @@ -8,7 +8,7 @@ slug: /concepts/message-sending リスナー関数の外でメッセージを送信したい場合や、より高度な処理(特定のエラーの処理など)を実行したい場合は、[Bolt インスタンスにアタッチされたクライアント](/concepts/web-api)の `client.chat_postMessage` を呼び出します。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'knock knock' が含まれるメッセージをリッスンし、イタリック体で 'Who's there?' と返信 @app.message("knock knock") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/opening-modals.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/opening-modals.md index ace4a620c..f2bc654a7 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/opening-modals.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/opening-modals.md @@ -10,7 +10,7 @@ slug: /concepts/opening-modals モーダルの生成方法についての詳細は、API ドキュメントを参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # ショートカットの呼び出しをリッスン diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/options.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/options.md index eba9ae299..4838b2a75 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/options.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/options.md @@ -12,9 +12,9 @@ slug: /concepts/options オプションのリクエストに応答するときは、有効なオプションを含む `options` または `option_groups` のリストとともに `ack()` を呼び出す必要があります。API サイトにある[外部データを使用する選択メニューに応答するサンプル例](https://api.slack.com/reference/messaging/block-elements#external-select)と、[ダイアログでの応答例](https://api.slack.com/dialogs#dynamic_select_elements_external)を参考にしてください。 -さらに、ユーザーが入力したキーワードに基づいたオプションを返すようフィルタリングロジックを適用することもできます。 これは `payload` という引数の ` value` の値に基づいて、それぞれのパターンで異なるオプションの一覧を返すように実装することができます。 Bolt for Python のすべてのリスナーやミドルウェアでは、[多くの有用な引数](https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html)にアクセスすることができますので、チェックしてみてください。 +さらに、ユーザーが入力したキーワードに基づいたオプションを返すようフィルタリングロジックを適用することもできます。 これは `payload` という引数の ` value` の値に基づいて、それぞれのパターンで異なるオプションの一覧を返すように実装することができます。 Bolt for Python のすべてのリスナーやミドルウェアでは、[多くの有用な引数](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html)にアクセスすることができますので、チェックしてみてください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 外部データを使用する選択メニューオプションに応答するサンプル例 @app.options("external_action") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/shortcuts.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/shortcuts.md index 170e34459..5824fbb65 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/shortcuts.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/shortcuts.md @@ -16,7 +16,7 @@ slug: /concepts/shortcuts ⚠️ グローバルショートカットのペイロードにはチャンネル ID が **含まれません**。アプリでチャンネル ID を取得する必要がある場合は、モーダル内に [`conversations_select`](https://api.slack.com/reference/block-kit/block-elements#conversation_select) エレメントを配置します。メッセージショートカットにはチャンネル ID が含まれます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'open_modal' という callback_id のショートカットをリッスン @app.shortcut("open_modal") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/updating-pushing-views.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/updating-pushing-views.md index a9de6d6a1..2948f978f 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/updating-pushing-views.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/updating-pushing-views.md @@ -16,7 +16,7 @@ slug: /concepts/updating-pushing-views モーダルの更新と多重表示に関する詳細は、API ドキュメントを参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # モーダルに含まれる、`button_abc` という action_id のボタンの呼び出しをリッスン diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/view_submissions.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/view_submissions.md index ef105683b..9e6d74058 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/view_submissions.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/view_submissions.md @@ -60,7 +60,7 @@ def handle_view_closed(ack, body, logger): logger.info(body) ``` -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # view_submission リクエストを処理 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/web-api.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/web-api.md index 5567b9687..0070ed0fb 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/web-api.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/web-api.md @@ -4,11 +4,11 @@ lang: ja-jp slug: /concepts/web-api --- -`app.client`、またはミドルウェア・リスナーの引数 `client` として Bolt アプリに提供されている [`WebClient`](https://slack.dev/python-slack-sdk/basic_usage.html) は必要な権限を付与されており、これを利用することで[あらゆる Web API メソッド](https://api.slack.com/methods)を呼び出すことができます。このクライアントのメソッドを呼び出すと `SlackResponse` という Slack からの応答情報を含むオブジェクトが返されます。 +`app.client`、またはミドルウェア・リスナーの引数 `client` として Bolt アプリに提供されている [`WebClient`](https://tools.slack.dev/python-slack-sdk/basic_usage.html) は必要な権限を付与されており、これを利用することで[あらゆる Web API メソッド](https://api.slack.com/methods)を呼び出すことができます。このクライアントのメソッドを呼び出すと `SlackResponse` という Slack からの応答情報を含むオブジェクトが返されます。 Bolt の初期化に使用するトークンは `context` オブジェクトに設定されます。このトークンは、多くの Web API メソッドを呼び出す際に必要となります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python @app.message("wake me up") def say_hello(client, message): diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/getting-started.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/getting-started.md index d1b6dab74..41aecf6ef 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/getting-started.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/getting-started.md @@ -180,7 +180,7 @@ app = App(token=os.environ.get("SLACK_BOT_TOKEN")) # 'こんにちは' を含むメッセージをリッスンします # 指定可能なリスナーのメソッド引数の一覧は以下のモジュールドキュメントを参考にしてください: -# https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html @app.message("こんにちは") def message_hello(message, say): # イベントがトリガーされたチャンネルへ say() でメッセージを送信します diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/adding-editing-steps.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/adding-editing-steps.md index 1787cc3e1..24b85bfa7 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/adding-editing-steps.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/adding-editing-steps.md @@ -12,7 +12,7 @@ slug: /concepts/adding-editing-steps 設定モーダルの開き方に関する詳細は、[こちらのドキュメント](https://api.slack.com/workflows/steps#handle_config_view)を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python def edit(ack, step, configure): diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/creating-steps.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/creating-steps.md index 2a827a352..889543767 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/creating-steps.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/creating-steps.md @@ -12,9 +12,9 @@ slug: /concepts/creating-steps `WorkflowStep` のインスタンスを作成したら、それを`app.step()` メソッドに渡します。これによって、アプリがワークフローステップのイベントをリッスンし、設定オブジェクトで指定されたコールバックを使ってそれに応答できるようになります。 -また、デコレーターとして利用できる `WorkflowStepBuilder` クラスを使ってワークフローステップを定義することもできます。 詳細は、[こちらのドキュメント](https://slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder)のコード例などを参考にしてください。 +また、デコレーターとして利用できる `WorkflowStepBuilder` クラスを使ってワークフローステップを定義することもできます。 詳細は、[こちらのドキュメント](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder)のコード例などを参考にしてください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python import os diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/executing-steps.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/executing-steps.md index b89921dee..e10c7eec3 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/executing-steps.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/executing-steps.md @@ -10,7 +10,7 @@ slug: /concepts/executing-steps `execute` コールバック内では、`complete()` を呼び出してステップの実行が成功したことを示すか、`fail()` を呼び出してステップの実行が失敗したことを示す必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python def execute(step, complete, fail): inputs = step["inputs"] diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/saving-steps.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/saving-steps.md index f27f8b59e..94ad32934 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/saving-steps.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/steps/saving-steps.md @@ -15,7 +15,7 @@ slug: /concepts/saving-steps これらのパラメータの構成方法に関する詳細は、[こちらのドキュメント](https://api.slack.com/reference/workflows/workflow_step)を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python def save(ack, view, update): diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/tutorial/getting-started-http.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/tutorial/getting-started-http.md index 007966677..b6c461de2 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/tutorial/getting-started-http.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/tutorial/getting-started-http.md @@ -179,7 +179,7 @@ app = App( # 'hello' を含むメッセージをリッスンします # 指定可能なリスナーのメソッド引数の一覧は以下のモジュールドキュメントを参考にしてください: -# https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html @app.message("hello") def message_hello(message, say): # イベントがトリガーされたチャンネルへ say() でメッセージを送信します diff --git a/docs/sidebars.js b/docs/sidebars.js index 609baa312..03c7106e5 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -89,7 +89,7 @@ const sidebars = { { type: 'link', label: 'Reference', - href: 'https://slack.dev/bolt-python/api-docs/slack_bolt/', + href: 'https://tools.slack.dev/bolt-python/api-docs/slack_bolt/', }, { type: 'html', value: '
    ' }, { From e322a288fa3228503dae7355f59ad4fcf4eedb63 Mon Sep 17 00:00:00 2001 From: Quinton Odenthal <127792999+Rat-Fiend@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:37:53 -0700 Subject: [PATCH 020/282] Update opening-modals.md (removed extra s) (#1161) All I did was remove an 's' after the "Modals link that seems like it isn't supposed to be there. --- docs/content/basic/opening-modals.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/basic/opening-modals.md b/docs/content/basic/opening-modals.md index 1e231bcc1..9049a5bdb 100644 --- a/docs/content/basic/opening-modals.md +++ b/docs/content/basic/opening-modals.md @@ -4,7 +4,7 @@ lang: en slug: /concepts/opening-modals --- -[Modals](https://api.slack.com/block-kit/surfaces/modal)s are focused surfaces that allow you to collect user data and display dynamic information. You can open a modal by passing a valid `trigger_id` and a [view payload](https://api.slack.com/reference/block-kit/views) to the built-in client's [`views.open`](https://api.slack.com/methods/views.open) method. +[Modals](https://api.slack.com/block-kit/surfaces/modal) are focused surfaces that allow you to collect user data and display dynamic information. You can open a modal by passing a valid `trigger_id` and a [view payload](https://api.slack.com/reference/block-kit/views) to the built-in client's [`views.open`](https://api.slack.com/methods/views.open) method. Your app receives `trigger_id`s in payloads sent to your Request URL that are triggered by user invocations, like a shortcut, button press, or interaction with a select menu. @@ -52,4 +52,4 @@ def open_modal(ack, body, client): ] } ) -``` \ No newline at end of file +``` From 736185adcc95b43553615b6653f1c336745a8d42 Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets <2201626+arkid15r@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:03:52 -0700 Subject: [PATCH 021/282] docs: simplify authorization.md Python code (#1164) --- docs/content/advanced/authorization.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/advanced/authorization.md b/docs/content/advanced/authorization.md index e350ba141..7a67d79ca 100644 --- a/docs/content/advanced/authorization.md +++ b/docs/content/advanced/authorization.md @@ -47,8 +47,8 @@ def authorize(enterprise_id, team_id, logger): # You can implement your own logic to fetch token here for team in installations: # enterprise_id doesn't exist for some teams - is_valid_enterprise = True if (("enterprise_id" not in team) or (enterprise_id == team["enterprise_id"])) else False - if ((is_valid_enterprise == True) and (team["team_id"] == team_id)): + is_valid_enterprise = "enterprise_id" not in team or enterprise_id == team["enterprise_id"] + if is_valid_enterprise and team["team_id"] == team_id: # Return an instance of AuthorizeResult # If you don't store bot_id and bot_user_id, could also call `from_auth_test_response` with your bot_token to automatically fetch them return AuthorizeResult( From c9599661ade31f2c450ead6171b1dd2434b1d178 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 19 Sep 2024 08:04:40 +0900 Subject: [PATCH 022/282] Update authorization.md --- .../current/advanced/authorization.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/authorization.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/authorization.md index 5b2e149fa..1a8797bb5 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/authorization.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/advanced/authorization.md @@ -47,8 +47,8 @@ def authorize(enterprise_id, team_id, logger): # トークンを取得するためのあなたのロジックをここに記述します for team in installations: # 一部のチームは enterprise_id を持たない場合があります - is_valid_enterprise = True if (("enterprise_id" not in team) or (enterprise_id == team["enterprise_id"])) else False - if ((is_valid_enterprise == True) and (team["team_id"] == team_id)): + is_valid_enterprise = "enterprise_id" not in team or enterprise_id == team["enterprise_id"] + if is_valid_enterprise and team["team_id"] == team_id: # AuthorizeResult のインスタンスを返します # bot_id と bot_user_id を保存していない場合、bot_token を使って `from_auth_test_response` を呼び出すと、自動的に取得できます return AuthorizeResult( @@ -65,4 +65,4 @@ app = App( signing_secret=os.environ["SLACK_SIGNING_SECRET"], authorize=authorize ) -``` \ No newline at end of file +``` From 35842a5b33033c7f7d1a41a008feefd059dde605 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:32:05 -0400 Subject: [PATCH 023/282] Bump express from 4.19.2 to 4.21.0 in /docs (#1165) Bumps [express](https://github.com/expressjs/express) from 4.19.2 to 4.21.0. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md) - [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 91 +++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 6a98f1108..2e5747440 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -4293,9 +4293,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -4305,7 +4305,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -5822,9 +5822,9 @@ } }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -6127,36 +6127,36 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -6192,9 +6192,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/express/node_modules/range-parser": { "version": "1.2.1", @@ -6379,12 +6379,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -8648,9 +8648,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -11743,11 +11746,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -12748,9 +12751,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -12783,6 +12786,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -12895,14 +12906,14 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" From 56d5ca389bb7529c71d81250a2e6cb8e422b423c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 27 Sep 2024 22:17:54 -0500 Subject: [PATCH 024/282] Fix double quoted img alt text for the "Add to Slack" button (#1170) --- slack_bolt/oauth/internals.py | 2 +- tests/adapter_tests_async/test_async_falcon.py | 2 +- tests/adapter_tests_async/test_async_fastapi.py | 2 +- tests/adapter_tests_async/test_async_sanic.py | 2 +- tests/adapter_tests_async/test_async_starlette.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/slack_bolt/oauth/internals.py b/slack_bolt/oauth/internals.py index da7a25263..05959817a 100644 --- a/slack_bolt/oauth/internals.py +++ b/slack_bolt/oauth/internals.py @@ -85,7 +85,7 @@ def _build_default_install_page_html(url: str) -> str:

    Slack App Installation

    -

    +

    Add to Slack

    """ # noqa: E501 diff --git a/tests/adapter_tests_async/test_async_falcon.py b/tests/adapter_tests_async/test_async_falcon.py index ada39307a..6e3901fdf 100644 --- a/tests/adapter_tests_async/test_async_falcon.py +++ b/tests/adapter_tests_async/test_async_falcon.py @@ -201,5 +201,5 @@ def test_oauth(self): response = client.simulate_get("/slack/install") assert response.status_code == 200 assert response.headers.get("content-type") == "text/html; charset=utf-8" - assert response.headers.get("content-length") == "609" + assert response.headers.get("content-length") == "607" assert "https://slack.com/oauth/v2/authorize?state=" in response.text diff --git a/tests/adapter_tests_async/test_async_fastapi.py b/tests/adapter_tests_async/test_async_fastapi.py index e3d92e1b9..311c802fe 100644 --- a/tests/adapter_tests_async/test_async_fastapi.py +++ b/tests/adapter_tests_async/test_async_fastapi.py @@ -209,7 +209,7 @@ async def endpoint(req: Request): response = client.get("/slack/install", allow_redirects=False) assert response.status_code == 200 assert response.headers.get("content-type") == "text/html; charset=utf-8" - assert response.headers.get("content-length") == "609" + assert response.headers.get("content-length") == "607" assert "https://slack.com/oauth/v2/authorize?state=" in response.text def test_custom_props(self): diff --git a/tests/adapter_tests_async/test_async_sanic.py b/tests/adapter_tests_async/test_async_sanic.py index 6a472704c..1b6bca8e2 100644 --- a/tests/adapter_tests_async/test_async_sanic.py +++ b/tests/adapter_tests_async/test_async_sanic.py @@ -221,6 +221,6 @@ async def endpoint(req: Request): # NOTE: Although sanic-testing 0.6 does not have this value, # Sanic apps properly generate the content-length header - # assert response.headers.get("content-length") == "609" + # assert response.headers.get("content-length") == "607" assert "https://slack.com/oauth/v2/authorize?state=" in response.text diff --git a/tests/adapter_tests_async/test_async_starlette.py b/tests/adapter_tests_async/test_async_starlette.py index d233dd8bb..db3a68a56 100644 --- a/tests/adapter_tests_async/test_async_starlette.py +++ b/tests/adapter_tests_async/test_async_starlette.py @@ -219,5 +219,5 @@ async def endpoint(req: Request): response = client.get("/slack/install", allow_redirects=False) assert response.status_code == 200 assert response.headers.get("content-type") == "text/html; charset=utf-8" - assert response.headers.get("content-length") == "609" + assert response.headers.get("content-length") == "607" assert "https://slack.com/oauth/v2/authorize?state=" in response.text From a09582c3caef0bb90840ac15d34e654844954922 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 17 Oct 2024 01:33:23 +0000 Subject: [PATCH 025/282] feat: expose auto acknowledge for `function` handlers (#1173) --- slack_bolt/app/app.py | 3 +- slack_bolt/app/async_app.py | 3 +- tests/scenario_tests/test_function.py | 37 ++++++++++++++++++++ tests/scenario_tests_async/test_function.py | 38 +++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index c72394821..f7761773e 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -877,6 +877,7 @@ def function( callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, + auto_acknowledge: bool = True, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -911,7 +912,7 @@ def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, True) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) return __call__ diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 92bad71b7..febf2464c 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -911,6 +911,7 @@ def function( callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, + auto_acknowledge: bool = True, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -947,7 +948,7 @@ def __call__(*args, **kwargs): primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, True) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) return __call__ diff --git a/tests/scenario_tests/test_function.py b/tests/scenario_tests/test_function.py index d00898082..00f0efba8 100644 --- a/tests/scenario_tests/test_function.py +++ b/tests/scenario_tests/test_function.py @@ -112,6 +112,32 @@ def test_invalid_declaration(self): with pytest.raises(TypeError): func("hello world") + def test_auto_acknowledge_false_with_acknowledging(self): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse", auto_acknowledge=False)(just_ack) + + request = self.build_request_from_body(function_body) + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + + def test_auto_acknowledge_false_without_acknowledging(self, caplog): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse", auto_acknowledge=False)(just_no_ack) + + request = self.build_request_from_body(function_body) + response = app.dispatch(request) + + assert response.status == 404 + assert_auth_test_count(self, 1) + assert f"WARNING {just_no_ack.__name__} didn't call ack()" in caplog.text + function_body = { "token": "verification_token", @@ -230,3 +256,14 @@ def complete_it(body, event, complete): assert body == function_body assert event == function_body["event"] complete(outputs={}) + + +def just_ack(ack, body, event): + assert body == function_body + assert event == function_body["event"] + ack() + + +def just_no_ack(body, event): + assert body == function_body + assert event == function_body["event"] diff --git a/tests/scenario_tests_async/test_function.py b/tests/scenario_tests_async/test_function.py index 0aefd7774..a2c10950c 100644 --- a/tests/scenario_tests_async/test_function.py +++ b/tests/scenario_tests_async/test_function.py @@ -116,6 +116,33 @@ async def test_invalid_callback_id(self): assert response.status == 404 await assert_auth_test_count_async(self, 1) + @pytest.mark.asyncio + async def test_auto_acknowledge_false_with_acknowledging(self): + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse", auto_acknowledge=False)(just_ack) + + request = self.build_request_from_body(function_body) + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + + @pytest.mark.asyncio + async def test_auto_acknowledge_false_without_acknowledging(self, caplog): + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse", auto_acknowledge=False)(just_no_ack) + + request = self.build_request_from_body(function_body) + response = await app.async_dispatch(request) + assert response.status == 404 + await assert_auth_test_count_async(self, 1) + assert f"WARNING {just_no_ack.__name__} didn't call ack()" in caplog.text + function_body = { "token": "verification_token", @@ -238,3 +265,14 @@ async def complete_it(body, event, complete): await complete( outputs={}, ) + + +async def just_ack(ack, body, event): + assert body == function_body + assert event == function_body["event"] + await ack() + + +async def just_no_ack(body, event): + assert body == function_body + assert event == function_body["event"] From 1a863715fdace5e59ef4e11b1ad606194e8a1c38 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 17 Oct 2024 10:33:38 +0900 Subject: [PATCH 026/282] Add Agents & Assistants feature (#1162) --- examples/assistants/app.py | 95 ++++++ examples/assistants/async_app.py | 97 ++++++ examples/assistants/async_interaction_app.py | 320 ++++++++++++++++++ examples/assistants/interaction_app.py | 155 +++++++++ requirements.txt | 2 +- slack_bolt/__init__.py | 21 ++ slack_bolt/app/app.py | 50 ++- slack_bolt/app/async_app.py | 46 ++- slack_bolt/async_app.py | 12 + slack_bolt/context/assistant/__init__.py | 1 + .../context/assistant/assistant_utilities.py | 81 +++++ .../assistant/async_assistant_utilities.py | 87 +++++ .../assistant/thread_context/__init__.py | 13 + .../thread_context_store/__init__.py | 1 + .../thread_context_store/async_store.py | 11 + .../default_async_store.py | 55 +++ .../thread_context_store/default_store.py | 50 +++ .../thread_context_store/file/__init__.py | 37 ++ .../assistant/thread_context_store/store.py | 11 + slack_bolt/context/async_context.py | 27 +- slack_bolt/context/base_context.py | 15 + slack_bolt/context/context.py | 27 +- .../context/get_thread_context/__init__.py | 6 + .../async_get_thread_context.py | 48 +++ .../get_thread_context/get_thread_context.py | 48 +++ .../context/save_thread_context/__init__.py | 6 + .../async_save_thread_context.py | 26 ++ .../save_thread_context.py | 26 ++ slack_bolt/context/say/async_say.py | 16 +- slack_bolt/context/say/say.py | 14 +- slack_bolt/context/set_status/__init__.py | 6 + .../context/set_status/async_set_status.py | 25 ++ slack_bolt/context/set_status/set_status.py | 25 ++ .../context/set_suggested_prompts/__init__.py | 6 + .../async_set_suggested_prompts.py | 34 ++ .../set_suggested_prompts.py | 34 ++ slack_bolt/context/set_title/__init__.py | 6 + .../context/set_title/async_set_title.py | 25 ++ slack_bolt/context/set_title/set_title.py | 25 ++ slack_bolt/kwargs_injection/args.py | 27 ++ slack_bolt/kwargs_injection/async_args.py | 27 ++ slack_bolt/kwargs_injection/async_utils.py | 5 + slack_bolt/kwargs_injection/utils.py | 4 + slack_bolt/listener/asyncio_runner.py | 4 + slack_bolt/listener/thread_runner.py | 5 + slack_bolt/middleware/__init__.py | 1 + slack_bolt/middleware/assistant/__init__.py | 6 + slack_bolt/middleware/assistant/assistant.py | 291 ++++++++++++++++ .../middleware/assistant/async_assistant.py | 320 ++++++++++++++++++ .../single_team_authorization.py | 1 + .../async_ignoring_self_events.py | 6 + .../ignoring_self_events.py | 13 +- slack_bolt/request/async_internals.py | 4 + slack_bolt/request/internals.py | 29 ++ slack_bolt/request/payload_utils.py | 67 ++++ slack_bolt/util/utils.py | 12 + tests/scenario_tests/test_events_assistant.py | 259 ++++++++++++++ .../test_events_assistant.py | 274 +++++++++++++++ 58 files changed, 2934 insertions(+), 11 deletions(-) create mode 100644 examples/assistants/app.py create mode 100644 examples/assistants/async_app.py create mode 100644 examples/assistants/async_interaction_app.py create mode 100644 examples/assistants/interaction_app.py create mode 100644 slack_bolt/context/assistant/__init__.py create mode 100644 slack_bolt/context/assistant/assistant_utilities.py create mode 100644 slack_bolt/context/assistant/async_assistant_utilities.py create mode 100644 slack_bolt/context/assistant/thread_context/__init__.py create mode 100644 slack_bolt/context/assistant/thread_context_store/__init__.py create mode 100644 slack_bolt/context/assistant/thread_context_store/async_store.py create mode 100644 slack_bolt/context/assistant/thread_context_store/default_async_store.py create mode 100644 slack_bolt/context/assistant/thread_context_store/default_store.py create mode 100644 slack_bolt/context/assistant/thread_context_store/file/__init__.py create mode 100644 slack_bolt/context/assistant/thread_context_store/store.py create mode 100644 slack_bolt/context/get_thread_context/__init__.py create mode 100644 slack_bolt/context/get_thread_context/async_get_thread_context.py create mode 100644 slack_bolt/context/get_thread_context/get_thread_context.py create mode 100644 slack_bolt/context/save_thread_context/__init__.py create mode 100644 slack_bolt/context/save_thread_context/async_save_thread_context.py create mode 100644 slack_bolt/context/save_thread_context/save_thread_context.py create mode 100644 slack_bolt/context/set_status/__init__.py create mode 100644 slack_bolt/context/set_status/async_set_status.py create mode 100644 slack_bolt/context/set_status/set_status.py create mode 100644 slack_bolt/context/set_suggested_prompts/__init__.py create mode 100644 slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py create mode 100644 slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py create mode 100644 slack_bolt/context/set_title/__init__.py create mode 100644 slack_bolt/context/set_title/async_set_title.py create mode 100644 slack_bolt/context/set_title/set_title.py create mode 100644 slack_bolt/middleware/assistant/__init__.py create mode 100644 slack_bolt/middleware/assistant/assistant.py create mode 100644 slack_bolt/middleware/assistant/async_assistant.py create mode 100644 tests/scenario_tests/test_events_assistant.py create mode 100644 tests/scenario_tests_async/test_events_assistant.py diff --git a/examples/assistants/app.py b/examples/assistants/app.py new file mode 100644 index 000000000..1c3a7a28a --- /dev/null +++ b/examples/assistants/app.py @@ -0,0 +1,95 @@ +import logging +import os +import time + +from slack_bolt.context.get_thread_context.get_thread_context import GetThreadContext + +logging.basicConfig(level=logging.DEBUG) + +from slack_bolt import App, Assistant, SetStatus, SetTitle, SetSuggestedPrompts, Say +from slack_bolt.adapter.socket_mode import SocketModeHandler + +app = App(token=os.environ["SLACK_BOT_TOKEN"]) + + +assistant = Assistant() +# You can use your own thread_context_store if you want +# from slack_bolt import FileAssistantThreadContextStore +# assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) + + +@assistant.thread_started +def start_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts): + say(":wave: Hi, how can I help you today?") + set_suggested_prompts( + prompts=[ + "What does SLACK stand for?", + "When Slack was released?", + ] + ) + + +@assistant.user_message(matchers=[lambda payload: "help page" in payload["text"]]) +def find_help_pages( + payload: dict, + logger: logging.Logger, + set_title: SetTitle, + set_status: SetStatus, + say: Say, +): + try: + set_title(payload["text"]) + set_status("Searching help pages...") + time.sleep(0.5) + say("Please check this help page: https://www.example.com/help-page-123") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +@assistant.user_message +def answer_other_inquiries( + payload: dict, + logger: logging.Logger, + set_title: SetTitle, + set_status: SetStatus, + say: Say, + get_thread_context: GetThreadContext, +): + try: + set_title(payload["text"]) + set_status("Typing...") + time.sleep(0.3) + set_status("Still typing...") + time.sleep(0.3) + thread_context = get_thread_context() + if thread_context is not None: + channel = thread_context.channel_id + say(f"Ah, you're referring to <#{channel}>! Do you need help with the channel?") + else: + say("Here you are! blah-blah-blah...") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +app.use(assistant) + + +@app.event("message") +def handle_message_in_channels(): + pass # noop + + +@app.event("app_mention") +def handle_non_assistant_thread_messages(say: Say): + say(":wave: I can help you out within our 1:1 DM!") + + +if __name__ == "__main__": + SocketModeHandler(app, app_token=os.environ["SLACK_APP_TOKEN"]).start() + +# pip install slack_bolt +# export SLACK_APP_TOKEN=xapp-*** +# export SLACK_BOT_TOKEN=xoxb-*** +# python app.py diff --git a/examples/assistants/async_app.py b/examples/assistants/async_app.py new file mode 100644 index 000000000..be7475a6f --- /dev/null +++ b/examples/assistants/async_app.py @@ -0,0 +1,97 @@ +import logging +import os +import asyncio + +from slack_bolt.context.get_thread_context.async_get_thread_context import AsyncGetThreadContext + +logging.basicConfig(level=logging.DEBUG) + +from slack_bolt.async_app import AsyncApp, AsyncAssistant, AsyncSetTitle, AsyncSetStatus, AsyncSetSuggestedPrompts, AsyncSay +from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler + +app = AsyncApp(token=os.environ["SLACK_BOT_TOKEN"]) + + +assistant = AsyncAssistant() + + +@assistant.thread_started +async def start_thread(say: AsyncSay, set_suggested_prompts: AsyncSetSuggestedPrompts): + await say(":wave: Hi, how can I help you today?") + await set_suggested_prompts( + prompts=[ + "What does SLACK stand for?", + "When Slack was released?", + ] + ) + + +@assistant.user_message(matchers=[lambda body: "help page" in body["event"]["text"]]) +async def find_help_pages( + payload: dict, + logger: logging.Logger, + set_title: AsyncSetTitle, + set_status: AsyncSetStatus, + say: AsyncSay, +): + try: + await set_title(payload["text"]) + await set_status("Searching help pages...") + await asyncio.sleep(0.5) + await say("Please check this help page: https://www.example.com/help-page-123") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + await say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +@assistant.user_message +async def answer_other_inquiries( + payload: dict, + logger: logging.Logger, + set_title: AsyncSetTitle, + set_status: AsyncSetStatus, + say: AsyncSay, + get_thread_context: AsyncGetThreadContext, +): + try: + await set_title(payload["text"]) + await set_status("Typing...") + await asyncio.sleep(0.3) + await set_status("Still typing...") + await asyncio.sleep(0.3) + thread_context = await get_thread_context() + if thread_context is not None: + channel = thread_context.channel_id + await say(f"Ah, you're referring to <#{channel}>! Do you need help with the channel?") + else: + await say("Here you are! blah-blah-blah...") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + await say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +app.use(assistant) + + +@app.event("message") +async def handle_message_in_channels(): + pass # noop + + +@app.event("app_mention") +async def handle_non_assistant_thread_messages(say: AsyncSay): + await say(":wave: I can help you out within our 1:1 DM!") + + +async def main(): + handler = AsyncSocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) + await handler.start_async() + + +if __name__ == "__main__": + asyncio.run(main()) + +# pip install slack_bolt aiohttp +# export SLACK_APP_TOKEN=xapp-*** +# export SLACK_BOT_TOKEN=xoxb-*** +# python async_app.py diff --git a/examples/assistants/async_interaction_app.py b/examples/assistants/async_interaction_app.py new file mode 100644 index 000000000..b9e8de3bc --- /dev/null +++ b/examples/assistants/async_interaction_app.py @@ -0,0 +1,320 @@ +# flake8: noqa F811 +import asyncio +import logging +import os +import random +import json + +logging.basicConfig(level=logging.DEBUG) + +from slack_bolt.async_app import AsyncApp, AsyncAssistant, AsyncSetStatus, AsyncSay, AsyncAck +from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler +from slack_sdk.web.async_client import AsyncWebClient + +app = AsyncApp( + token=os.environ["SLACK_BOT_TOKEN"], + # This must be set to handle bot message events + ignoring_self_assistant_message_events_enabled=False, +) + + +assistant = AsyncAssistant() +# You can use your own thread_context_store if you want +# from slack_bolt import FileAssistantThreadContextStore +# assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) + + +@assistant.thread_started +async def start_thread(say: AsyncSay): + await say( + text=":wave: Hi, how can I help you today?", + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": ":wave: Hi, how can I help you today?"}, + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "action_id": "assistant-generate-random-numbers", + "text": {"type": "plain_text", "text": "Generate random numbers"}, + "value": "1", + }, + ], + }, + ], + ) + + +@app.action("assistant-generate-random-numbers") +async def configure_assistant_summarize_channel(ack: AsyncAck, client: AsyncWebClient, body: dict): + await ack() + await client.views_open( + trigger_id=body["trigger_id"], + view={ + "type": "modal", + "callback_id": "configure_assistant_summarize_channel", + "title": {"type": "plain_text", "text": "My Assistant"}, + "submit": {"type": "plain_text", "text": "Submit"}, + "close": {"type": "plain_text", "text": "Cancel"}, + "private_metadata": json.dumps( + { + "channel_id": body["channel"]["id"], + "thread_ts": body["message"]["thread_ts"], + } + ), + "blocks": [ + { + "type": "input", + "block_id": "num", + "label": {"type": "plain_text", "text": "# of outputs"}, + "element": { + "type": "static_select", + "action_id": "input", + "placeholder": {"type": "plain_text", "text": "How many numbers do you need?"}, + "options": [ + {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + {"text": {"type": "plain_text", "text": "10"}, "value": "10"}, + {"text": {"type": "plain_text", "text": "20"}, "value": "20"}, + ], + "initial_option": {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + }, + } + ], + }, + ) + + +@app.view("configure_assistant_summarize_channel") +async def receive_configure_assistant_summarize_channel(ack: AsyncAck, client: AsyncWebClient, payload: dict): + await ack() + num = payload["state"]["values"]["num"]["input"]["selected_option"]["value"] + thread = json.loads(payload["private_metadata"]) + await client.chat_postMessage( + channel=thread["channel_id"], + thread_ts=thread["thread_ts"], + text=f"OK, you need {num} numbers. I will generate it shortly!", + metadata={ + "event_type": "assistant-generate-random-numbers", + "event_payload": {"num": int(num)}, + }, + ) + + +@assistant.bot_message +async def respond_to_bot_messages(logger: logging.Logger, set_status: AsyncSetStatus, say: AsyncSay, payload: dict): + try: + if payload.get("metadata", {}).get("event_type") == "assistant-generate-random-numbers": + await set_status("is generating an array of random numbers...") + await asyncio.sleep(1) + nums: Set[str] = set() + num = payload["metadata"]["event_payload"]["num"] + while len(nums) < num: + nums.add(str(random.randint(1, 100))) + await say(f"Here you are: {', '.join(nums)}") + else: + # nothing to do for this bot message + # If you want to add more patterns here, be careful not to cause infinite loop messaging + pass + + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + + +@assistant.user_message +async def respond_to_user_messages(logger: logging.Logger, set_status: AsyncSetStatus, say: AsyncSay): + try: + await set_status("is typing...") + await say("Sorry, I couldn't understand your comment. Could you say it in a different way?") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + await say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +app.use(assistant) + + +@app.event("message") +async def handle_message_in_channels(): + pass # noop + + +@app.event("app_mention") +async def handle_non_assistant_thread_messages(say: AsyncSay): + await say(":wave: I can help you out within our 1:1 DM!") + + +async def main(): + handler = AsyncSocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) + await handler.start_async() + + +if __name__ == "__main__": + asyncio.run(main()) + +# pip install slack_bolt aiohttp +# export SLACK_APP_TOKEN=xapp-*** +# export SLACK_BOT_TOKEN=xoxb-*** +# python async_interaction_app.py +import asyncio +import json +import logging +import os +from typing import Set +import random + +logging.basicConfig(level=logging.DEBUG) + +from slack_bolt.async_app import AsyncApp, AsyncAssistant, AsyncSetStatus, AsyncSay, AsyncAck +from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler +from slack_sdk.web.async_client import AsyncWebClient + +app = AsyncApp( + token=os.environ["SLACK_BOT_TOKEN"], + # This must be set to handle bot message events + ignoring_self_assistant_message_events_enabled=False, +) + + +assistant = AsyncAssistant() +# You can use your own thread_context_store if you want +# from slack_bolt import FileAssistantThreadContextStore +# assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) + + +@assistant.thread_started +async def start_thread(say: AsyncSay): + await say( + text=":wave: Hi, how can I help you today?", + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": ":wave: Hi, how can I help you today?"}, + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "action_id": "assistant-generate-random-numbers", + "text": {"type": "plain_text", "text": "Generate random numbers"}, + "value": "1", + }, + ], + }, + ], + ) + + +@app.action("assistant-generate-random-numbers") +async def configure_assistant_summarize_channel(ack: AsyncAck, client: AsyncWebClient, body: dict): + await ack() + await client.views_open( + trigger_id=body["trigger_id"], + view={ + "type": "modal", + "callback_id": "configure_assistant_summarize_channel", + "title": {"type": "plain_text", "text": "My Assistant"}, + "submit": {"type": "plain_text", "text": "Submit"}, + "close": {"type": "plain_text", "text": "Cancel"}, + "private_metadata": json.dumps( + { + "channel_id": body["channel"]["id"], + "thread_ts": body["message"]["thread_ts"], + } + ), + "blocks": [ + { + "type": "input", + "block_id": "num", + "label": {"type": "plain_text", "text": "# of outputs"}, + "element": { + "type": "static_select", + "action_id": "input", + "placeholder": {"type": "plain_text", "text": "How many numbers do you need?"}, + "options": [ + {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + {"text": {"type": "plain_text", "text": "10"}, "value": "10"}, + {"text": {"type": "plain_text", "text": "20"}, "value": "20"}, + ], + "initial_option": {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + }, + } + ], + }, + ) + + +@app.view("configure_assistant_summarize_channel") +async def receive_configure_assistant_summarize_channel(ack: AsyncAck, client: AsyncWebClient, payload: dict): + await ack() + num = payload["state"]["values"]["num"]["input"]["selected_option"]["value"] + thread = json.loads(payload["private_metadata"]) + await client.chat_postMessage( + channel=thread["channel_id"], + thread_ts=thread["thread_ts"], + text=f"OK, you need {num} numbers. I will generate it shortly!", + metadata={ + "event_type": "assistant-generate-random-numbers", + "event_payload": {"num": int(num)}, + }, + ) + + +@assistant.bot_message +async def respond_to_bot_messages(logger: logging.Logger, set_status: AsyncSetStatus, say: AsyncSay, payload: dict): + try: + if payload.get("metadata", {}).get("event_type") == "assistant-generate-random-numbers": + await set_status("is generating an array of random numbers...") + await asyncio.sleep(1) + nums: Set[str] = set() + num = payload["metadata"]["event_payload"]["num"] + while len(nums) < num: + nums.add(str(random.randint(1, 100))) + await say(f"Here you are: {', '.join(nums)}") + else: + # nothing to do for this bot message + # If you want to add more patterns here, be careful not to cause infinite loop messaging + pass + + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + + +@assistant.user_message +async def respond_to_user_messages(logger: logging.Logger, set_status: AsyncSetStatus, say: AsyncSay): + try: + await set_status("is typing...") + await say("Sorry, I couldn't understand your comment. Could you say it in a different way?") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + await say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +app.use(assistant) + + +@app.event("message") +async def handle_message_in_channels(): + pass # noop + + +@app.event("app_mention") +async def handle_non_assistant_thread_messages(say: AsyncSay): + await say(":wave: I can help you out within our 1:1 DM!") + + +async def main(): + handler = AsyncSocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) + await handler.start_async() + + +if __name__ == "__main__": + asyncio.run(main()) + +# pip install slack_bolt aiohttp +# export SLACK_APP_TOKEN=xapp-*** +# export SLACK_BOT_TOKEN=xoxb-*** +# python async_interaction_app.py diff --git a/examples/assistants/interaction_app.py b/examples/assistants/interaction_app.py new file mode 100644 index 000000000..101035739 --- /dev/null +++ b/examples/assistants/interaction_app.py @@ -0,0 +1,155 @@ +import json +import logging +import os +from typing import Set +import random +import time + +logging.basicConfig(level=logging.DEBUG) + +from slack_bolt import App, Assistant, SetStatus, Say, Ack +from slack_bolt.adapter.socket_mode import SocketModeHandler +from slack_sdk import WebClient + +app = App( + token=os.environ["SLACK_BOT_TOKEN"], + # This must be set to handle bot message events + ignoring_self_assistant_message_events_enabled=False, +) + + +assistant = Assistant() +# You can use your own thread_context_store if you want +# from slack_bolt import FileAssistantThreadContextStore +# assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) + + +@assistant.thread_started +def start_thread(say: Say): + say( + text=":wave: Hi, how can I help you today?", + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": ":wave: Hi, how can I help you today?"}, + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "action_id": "assistant-generate-random-numbers", + "text": {"type": "plain_text", "text": "Generate random numbers"}, + "value": "1", + }, + ], + }, + ], + ) + + +@app.action("assistant-generate-random-numbers") +def configure_assistant_summarize_channel(ack: Ack, client: WebClient, body: dict): + ack() + client.views_open( + trigger_id=body["trigger_id"], + view={ + "type": "modal", + "callback_id": "configure_assistant_summarize_channel", + "title": {"type": "plain_text", "text": "My Assistant"}, + "submit": {"type": "plain_text", "text": "Submit"}, + "close": {"type": "plain_text", "text": "Cancel"}, + "private_metadata": json.dumps( + { + "channel_id": body["channel"]["id"], + "thread_ts": body["message"]["thread_ts"], + } + ), + "blocks": [ + { + "type": "input", + "block_id": "num", + "label": {"type": "plain_text", "text": "# of outputs"}, + "element": { + "type": "static_select", + "action_id": "input", + "placeholder": {"type": "plain_text", "text": "How many numbers do you need?"}, + "options": [ + {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + {"text": {"type": "plain_text", "text": "10"}, "value": "10"}, + {"text": {"type": "plain_text", "text": "20"}, "value": "20"}, + ], + "initial_option": {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + }, + } + ], + }, + ) + + +@app.view("configure_assistant_summarize_channel") +def receive_configure_assistant_summarize_channel(ack: Ack, client: WebClient, payload: dict): + ack() + num = payload["state"]["values"]["num"]["input"]["selected_option"]["value"] + thread = json.loads(payload["private_metadata"]) + client.chat_postMessage( + channel=thread["channel_id"], + thread_ts=thread["thread_ts"], + text=f"OK, you need {num} numbers. I will generate it shortly!", + metadata={ + "event_type": "assistant-generate-random-numbers", + "event_payload": {"num": int(num)}, + }, + ) + + +@assistant.bot_message +def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: Say, payload: dict): + try: + if payload.get("metadata", {}).get("event_type") == "assistant-generate-random-numbers": + set_status("is generating an array of random numbers...") + time.sleep(1) + nums: Set[str] = set() + num = payload["metadata"]["event_payload"]["num"] + while len(nums) < num: + nums.add(str(random.randint(1, 100))) + say(f"Here you are: {', '.join(nums)}") + else: + # nothing to do for this bot message + # If you want to add more patterns here, be careful not to cause infinite loop messaging + pass + + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + + +@assistant.user_message +def respond_to_user_messages(logger: logging.Logger, set_status: SetStatus, say: Say): + try: + set_status("is typing...") + say("Sorry, I couldn't understand your comment. Could you say it in a different way?") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +app.use(assistant) + + +@app.event("message") +def handle_message_in_channels(): + pass # noop + + +@app.event("app_mention") +def handle_non_assistant_thread_messages(say: Say): + say(":wave: I can help you out within our 1:1 DM!") + + +if __name__ == "__main__": + SocketModeHandler(app, app_token=os.environ["SLACK_APP_TOKEN"]).start() + +# pip install slack_bolt +# export SLACK_APP_TOKEN=xapp-*** +# export SLACK_BOT_TOKEN=xoxb-*** +# python interaction_app.py diff --git a/requirements.txt b/requirements.txt index e2980e2d6..bdf4a1191 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -slack_sdk>=3.26.0,<4 +slack_sdk>=3.33.1,<4 diff --git a/slack_bolt/__init__.py b/slack_bolt/__init__.py index 789b93c92..32ab76721 100644 --- a/slack_bolt/__init__.py +++ b/slack_bolt/__init__.py @@ -20,6 +20,19 @@ from .request import BoltRequest from .response import BoltResponse +# AI Agents & Assistants +from .middleware.assistant.assistant import ( + Assistant, +) +from .context.assistant.thread_context import AssistantThreadContext +from .context.assistant.thread_context_store.store import AssistantThreadContextStore +from .context.assistant.thread_context_store.file import FileAssistantThreadContextStore + +from .context.set_status import SetStatus +from .context.set_title import SetTitle +from .context.set_suggested_prompts import SetSuggestedPrompts +from .context.save_thread_context import SaveThreadContext + __all__ = [ "App", "BoltContext", @@ -33,4 +46,12 @@ "CustomListenerMatcher", "BoltRequest", "BoltResponse", + "Assistant", + "AssistantThreadContext", + "AssistantThreadContextStore", + "FileAssistantThreadContextStore", + "SetStatus", + "SetTitle", + "SetSuggestedPrompts", + "SaveThreadContext", ] diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index f7761773e..3d5532b7b 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -19,6 +19,10 @@ InstallationStoreAuthorize, CallableAuthorize, ) + +from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore + +from slack_bolt.context.assistant.assistant_utilities import AssistantUtilities from slack_bolt.error import BoltError, BoltUnhandledRequestError from slack_bolt.lazy_listener.thread_runner import ThreadLazyListenerRunner from slack_bolt.listener.builtins import TokenRevocationListeners @@ -66,6 +70,7 @@ CustomMiddleware, AttachingFunctionToken, ) +from slack_bolt.middleware.assistant import Assistant from slack_bolt.middleware.message_listener_matches import MessageListenerMatches from slack_bolt.middleware.middleware_error_handler import ( DefaultMiddlewareErrorHandler, @@ -77,6 +82,10 @@ from slack_bolt.oauth.internals import select_consistent_installation_store from slack_bolt.oauth.oauth_settings import OAuthSettings from slack_bolt.request import BoltRequest +from slack_bolt.request.payload_utils import ( + is_assistant_event, + to_event, +) from slack_bolt.response import BoltResponse from slack_bolt.util.utils import ( create_web_client, @@ -114,6 +123,7 @@ def __init__( # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -124,6 +134,8 @@ def __init__( verification_token: Optional[str] = None, # Set this one only when you want to customize the executor listener_executor: Optional[Executor] = None, + # for AI Agents & Assistants + assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -179,6 +191,9 @@ def message_hello(message, say): ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). + ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. + `IgnoringSelfEvents` for this app's bot user message events within an assistant thread + This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. @@ -192,6 +207,8 @@ def message_hello(message, say): verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. + assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, + which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") @@ -338,6 +355,8 @@ def message_hello(message, say): if listener_executor is None: listener_executor = ThreadPoolExecutor(max_workers=5) + self._assistant_thread_context_store = assistant_thread_context_store + self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( logger=self._framework_logger, @@ -360,6 +379,7 @@ def message_hello(message, say): token_verification_enabled=token_verification_enabled, request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, @@ -371,6 +391,7 @@ def _init_middleware_list( token_verification_enabled: bool = True, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -431,7 +452,12 @@ def _init_middleware_list( raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: - self._middleware_list.append(IgnoringSelfEvents(base_logger=self._base_logger)) + self._middleware_list.append( + IgnoringSelfEvents( + base_logger=self._base_logger, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, + ) + ) if url_verification_enabled is True: self._middleware_list.append(UrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: @@ -656,6 +682,8 @@ def middleware_func(logger, body, next): if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) + if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: + self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( @@ -669,6 +697,12 @@ def middleware_func(logger, body, next): raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None + # ------------------------- + # AI Agents & Assistants + + def assistant(self, assistant: Assistant) -> Optional[Callable]: + return self.middleware(assistant) + # ------------------------- # Workflows: Steps from apps @@ -1355,6 +1389,20 @@ def _init_context(self, req: BoltRequest): # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner + # For AI Agents & Assistants + if is_assistant_event(req.body): + assistant = AssistantUtilities( + payload=to_event(req.body), # type:ignore[arg-type] + context=req.context, + thread_context_store=self._assistant_thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + @staticmethod def _to_listener_functions( kwargs: dict, diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index febf2464c..50a36e5dd 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -8,6 +8,10 @@ from aiohttp import web from slack_bolt.app.async_server import AsyncSlackAppServer +from slack_bolt.context.assistant.async_assistant_utilities import AsyncAssistantUtilities +from slack_bolt.context.assistant.thread_context_store.async_store import ( + AsyncAssistantThreadContextStore, +) from slack_bolt.listener.async_builtins import AsyncTokenRevocationListeners from slack_bolt.listener.async_listener_start_handler import ( AsyncDefaultListenerStartHandler, @@ -16,6 +20,7 @@ AsyncDefaultListenerCompletionHandler, ) from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner +from slack_bolt.middleware.assistant.async_assistant import AsyncAssistant from slack_bolt.middleware.async_middleware_error_handler import ( AsyncCustomMiddlewareErrorHandler, AsyncDefaultMiddlewareErrorHandler, @@ -25,6 +30,7 @@ AsyncMessageListenerMatches, ) from slack_bolt.oauth.async_internals import select_consistent_installation_store +from slack_bolt.request.payload_utils import is_assistant_event, to_event from slack_bolt.util.utils import get_name_for_callable, is_callable_coroutine from slack_bolt.workflows.step.async_step import ( AsyncWorkflowStep, @@ -125,6 +131,7 @@ def __init__( # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -133,6 +140,8 @@ def __init__( oauth_flow: Optional[AsyncOAuthFlow] = None, # No need to set (the value is used only in response to ssl_check requests) verification_token: Optional[str] = None, + # for AI Agents & Assistants + assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -187,6 +196,9 @@ async def message_hello(message, say): # async function ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `AsyncIgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). + ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. + `IgnoringSelfEvents` for this app's bot user message events within an assistant thread + This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `AsyncUrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. @@ -197,7 +209,9 @@ async def message_hello(message, say): # async function when your app receives `function_executed` or interactivity events scoped to a custom step. oauth_settings: The settings related to Slack app installation flow (OAuth flow) oauth_flow: Instantiated `slack_bolt.oauth.AsyncOAuthFlow`. This is always prioritized over oauth_settings. - verification_token: Deprecated verification mechanism. This can used only for ssl_check requests. + verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. + assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, + which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") @@ -347,6 +361,8 @@ async def message_hello(message, say): # async function self._async_middleware_list: List[AsyncMiddleware] = [] self._async_listeners: List[AsyncListener] = [] + self._assistant_thread_context_store = assistant_thread_context_store + self._process_before_response = process_before_response self._async_listener_runner = AsyncioListenerRunner( logger=self._framework_logger, @@ -366,6 +382,7 @@ async def message_hello(message, say): # async function self._init_async_middleware_list( request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, @@ -378,6 +395,7 @@ def _init_async_middleware_list( self, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -430,7 +448,12 @@ def _init_async_middleware_list( raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: - self._async_middleware_list.append(AsyncIgnoringSelfEvents(base_logger=self._base_logger)) + self._async_middleware_list.append( + AsyncIgnoringSelfEvents( + base_logger=self._base_logger, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, + ) + ) if url_verification_enabled is True: self._async_middleware_list.append(AsyncUrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: @@ -683,6 +706,8 @@ async def middleware_func(logger, body, next): if isinstance(middleware_or_callable, AsyncMiddleware): middleware: AsyncMiddleware = middleware_or_callable self._async_middleware_list.append(middleware) + if isinstance(middleware, AsyncAssistant) and middleware.thread_context_store is not None: + self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._async_middleware_list.append( AsyncCustomMiddleware( @@ -696,6 +721,9 @@ async def middleware_func(logger, body, next): raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None + def assistant(self, assistant: AsyncAssistant) -> Optional[Callable]: + return self.middleware(assistant) + # ------------------------- # Workflows: Steps from apps @@ -1395,6 +1423,20 @@ def _init_context(self, req: AsyncBoltRequest): # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner + # For AI Agents & Assistants + if is_assistant_event(req.body): + assistant = AsyncAssistantUtilities( + payload=to_event(req.body), # type:ignore[arg-type] + context=req.context, + thread_context_store=self._assistant_thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + @staticmethod def _to_listener_functions( kwargs: dict, diff --git a/slack_bolt/async_app.py b/slack_bolt/async_app.py index 3157e8006..10878c51b 100644 --- a/slack_bolt/async_app.py +++ b/slack_bolt/async_app.py @@ -53,6 +53,12 @@ async def command(ack, body, respond): from .listener.async_listener import AsyncListener from .listener_matcher.async_listener_matcher import AsyncCustomListenerMatcher from .request.async_request import AsyncBoltRequest +from .middleware.assistant.async_assistant import AsyncAssistant +from .context.set_status.async_set_status import AsyncSetStatus +from .context.set_title.async_set_title import AsyncSetTitle +from .context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts +from .context.get_thread_context.async_get_thread_context import AsyncGetThreadContext +from .context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext __all__ = [ "AsyncApp", @@ -63,4 +69,10 @@ async def command(ack, body, respond): "AsyncListener", "AsyncCustomListenerMatcher", "AsyncBoltRequest", + "AsyncAssistant", + "AsyncSetStatus", + "AsyncSetTitle", + "AsyncSetSuggestedPrompts", + "AsyncGetThreadContext", + "AsyncSaveThreadContext", ] diff --git a/slack_bolt/context/assistant/__init__.py b/slack_bolt/context/assistant/__init__.py new file mode 100644 index 000000000..c761cec3a --- /dev/null +++ b/slack_bolt/context/assistant/__init__.py @@ -0,0 +1 @@ +# Don't add async module imports here diff --git a/slack_bolt/context/assistant/assistant_utilities.py b/slack_bolt/context/assistant/assistant_utilities.py new file mode 100644 index 000000000..6746ec286 --- /dev/null +++ b/slack_bolt/context/assistant/assistant_utilities.py @@ -0,0 +1,81 @@ +from typing import Optional + +from slack_sdk.web import WebClient +from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore +from slack_bolt.context.assistant.thread_context_store.default_store import DefaultAssistantThreadContextStore + + +from slack_bolt.context.context import BoltContext +from slack_bolt.context.say import Say +from ..get_thread_context.get_thread_context import GetThreadContext +from ..save_thread_context import SaveThreadContext +from ..set_status import SetStatus +from ..set_suggested_prompts import SetSuggestedPrompts +from ..set_title import SetTitle + + +class AssistantUtilities: + payload: dict + client: WebClient + channel_id: str + thread_ts: str + thread_context_store: AssistantThreadContextStore + + def __init__( + self, + *, + payload: dict, + context: BoltContext, + thread_context_store: Optional[AssistantThreadContextStore] = None, + ): + self.payload = payload + self.client = context.client + self.thread_context_store = thread_context_store or DefaultAssistantThreadContextStore(context) + + if self.payload.get("assistant_thread") is not None: + # assistant_thread_started + thread = self.payload["assistant_thread"] + self.channel_id = thread["channel_id"] + self.thread_ts = thread["thread_ts"] + elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: + # message event + self.channel_id = self.payload["channel"] + self.thread_ts = self.payload["thread_ts"] + else: + # When moving this code to Bolt internals, no need to raise an exception for this pattern + raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})") + + def is_valid(self) -> bool: + return self.channel_id is not None and self.thread_ts is not None + + @property + def set_status(self) -> SetStatus: + return SetStatus(self.client, self.channel_id, self.thread_ts) + + @property + def set_title(self) -> SetTitle: + return SetTitle(self.client, self.channel_id, self.thread_ts) + + @property + def set_suggested_prompts(self) -> SetSuggestedPrompts: + return SetSuggestedPrompts(self.client, self.channel_id, self.thread_ts) + + @property + def say(self) -> Say: + return Say( + self.client, + channel=self.channel_id, + thread_ts=self.thread_ts, + metadata={ + "event_type": "assistant_thread_context", + "event_payload": self.get_thread_context(), + }, + ) + + @property + def get_thread_context(self) -> GetThreadContext: + return GetThreadContext(self.thread_context_store, self.channel_id, self.thread_ts, self.payload) + + @property + def save_thread_context(self) -> SaveThreadContext: + return SaveThreadContext(self.thread_context_store, self.channel_id, self.thread_ts) diff --git a/slack_bolt/context/assistant/async_assistant_utilities.py b/slack_bolt/context/assistant/async_assistant_utilities.py new file mode 100644 index 000000000..b0f8a1fae --- /dev/null +++ b/slack_bolt/context/assistant/async_assistant_utilities.py @@ -0,0 +1,87 @@ +from typing import Optional + +from slack_sdk.web.async_client import AsyncWebClient +from slack_bolt.context.assistant.thread_context_store.async_store import ( + AsyncAssistantThreadContextStore, +) + +from slack_bolt.context.assistant.thread_context_store.default_async_store import DefaultAsyncAssistantThreadContextStore + + +from slack_bolt.context.async_context import AsyncBoltContext +from slack_bolt.context.say.async_say import AsyncSay +from ..get_thread_context.async_get_thread_context import AsyncGetThreadContext +from ..save_thread_context.async_save_thread_context import AsyncSaveThreadContext +from ..set_status.async_set_status import AsyncSetStatus +from ..set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts +from ..set_title.async_set_title import AsyncSetTitle + + +class AsyncAssistantUtilities: + payload: dict + client: AsyncWebClient + channel_id: str + thread_ts: str + thread_context_store: AsyncAssistantThreadContextStore + + def __init__( + self, + *, + payload: dict, + context: AsyncBoltContext, + thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, + ): + self.payload = payload + self.client = context.client + self.thread_context_store = thread_context_store or DefaultAsyncAssistantThreadContextStore(context) + + if self.payload.get("assistant_thread") is not None: + # assistant_thread_started + thread = self.payload["assistant_thread"] + self.channel_id = thread["channel_id"] + self.thread_ts = thread["thread_ts"] + elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: + # message event + self.channel_id = self.payload["channel"] + self.thread_ts = self.payload["thread_ts"] + else: + # When moving this code to Bolt internals, no need to raise an exception for this pattern + raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})") + + def is_valid(self) -> bool: + return self.channel_id is not None and self.thread_ts is not None + + @property + def set_status(self) -> AsyncSetStatus: + return AsyncSetStatus(self.client, self.channel_id, self.thread_ts) + + @property + def set_title(self) -> AsyncSetTitle: + return AsyncSetTitle(self.client, self.channel_id, self.thread_ts) + + @property + def set_suggested_prompts(self) -> AsyncSetSuggestedPrompts: + return AsyncSetSuggestedPrompts(self.client, self.channel_id, self.thread_ts) + + @property + def say(self) -> AsyncSay: + return AsyncSay( + self.client, + channel=self.channel_id, + thread_ts=self.thread_ts, + build_metadata=self._build_message_metadata, + ) + + async def _build_message_metadata(self) -> dict: + return { + "event_type": "assistant_thread_context", + "event_payload": await self.get_thread_context(), + } + + @property + def get_thread_context(self) -> AsyncGetThreadContext: + return AsyncGetThreadContext(self.thread_context_store, self.channel_id, self.thread_ts, self.payload) + + @property + def save_thread_context(self) -> AsyncSaveThreadContext: + return AsyncSaveThreadContext(self.thread_context_store, self.channel_id, self.thread_ts) diff --git a/slack_bolt/context/assistant/thread_context/__init__.py b/slack_bolt/context/assistant/thread_context/__init__.py new file mode 100644 index 000000000..bfa97feeb --- /dev/null +++ b/slack_bolt/context/assistant/thread_context/__init__.py @@ -0,0 +1,13 @@ +from typing import Optional + + +class AssistantThreadContext(dict): + enterprise_id: Optional[str] + team_id: Optional[str] + channel_id: str + + def __init__(self, payload: dict): + dict.__init__(self, **payload) + self.enterprise_id = payload.get("enterprise_id") + self.team_id = payload.get("team_id") + self.channel_id = payload["channel_id"] diff --git a/slack_bolt/context/assistant/thread_context_store/__init__.py b/slack_bolt/context/assistant/thread_context_store/__init__.py new file mode 100644 index 000000000..c761cec3a --- /dev/null +++ b/slack_bolt/context/assistant/thread_context_store/__init__.py @@ -0,0 +1 @@ +# Don't add async module imports here diff --git a/slack_bolt/context/assistant/thread_context_store/async_store.py b/slack_bolt/context/assistant/thread_context_store/async_store.py new file mode 100644 index 000000000..51c0d6691 --- /dev/null +++ b/slack_bolt/context/assistant/thread_context_store/async_store.py @@ -0,0 +1,11 @@ +from typing import Dict, Optional + +from slack_bolt.context.assistant.thread_context import AssistantThreadContext + + +class AsyncAssistantThreadContextStore: + async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: + raise NotImplementedError() + + async def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: + raise NotImplementedError() diff --git a/slack_bolt/context/assistant/thread_context_store/default_async_store.py b/slack_bolt/context/assistant/thread_context_store/default_async_store.py new file mode 100644 index 000000000..351f558d2 --- /dev/null +++ b/slack_bolt/context/assistant/thread_context_store/default_async_store.py @@ -0,0 +1,55 @@ +from typing import Dict, Optional, List + +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.context.async_context import AsyncBoltContext + +from slack_bolt.context.assistant.thread_context import AssistantThreadContext +from slack_bolt.context.assistant.thread_context_store.async_store import ( + AsyncAssistantThreadContextStore, +) + + +class DefaultAsyncAssistantThreadContextStore(AsyncAssistantThreadContextStore): + client: AsyncWebClient + context: AsyncBoltContext + + def __init__(self, context: AsyncBoltContext): + self.client = context.client + self.context = context + + async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: + parent_message = await self._retrieve_first_bot_reply(channel_id, thread_ts) + if parent_message is not None: + await self.client.chat_update( + channel=channel_id, + ts=parent_message["ts"], + text=parent_message["text"], + blocks=parent_message["blocks"], + metadata={ + "event_type": "assistant_thread_context", + "event_payload": context, + }, + ) + + async def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: + parent_message = await self._retrieve_first_bot_reply(channel_id, thread_ts) + if parent_message is not None and parent_message.get("metadata"): + if bool(parent_message["metadata"]["event_payload"]): + return AssistantThreadContext(parent_message["metadata"]["event_payload"]) + return None + + async def _retrieve_first_bot_reply(self, channel_id: str, thread_ts: str) -> Optional[dict]: + messages: List[dict] = ( + await self.client.conversations_replies( + channel=channel_id, + ts=thread_ts, + oldest=thread_ts, + include_all_metadata=True, + limit=4, # 2 should be usually enough but buffer for more robustness + ) + ).get("messages", []) + for message in messages: + if message.get("subtype") is None and message.get("user") == self.context.bot_user_id: + return message + return None diff --git a/slack_bolt/context/assistant/thread_context_store/default_store.py b/slack_bolt/context/assistant/thread_context_store/default_store.py new file mode 100644 index 000000000..9b9490737 --- /dev/null +++ b/slack_bolt/context/assistant/thread_context_store/default_store.py @@ -0,0 +1,50 @@ +from typing import Dict, Optional, List + +from slack_bolt.context.context import BoltContext +from slack_sdk import WebClient + +from slack_bolt.context.assistant.thread_context import AssistantThreadContext +from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore + + +class DefaultAssistantThreadContextStore(AssistantThreadContextStore): + client: WebClient + context: "BoltContext" + + def __init__(self, context: BoltContext): + self.client = context.client + self.context = context + + def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: + parent_message = self._retrieve_first_bot_reply(channel_id, thread_ts) + if parent_message is not None: + self.client.chat_update( + channel=channel_id, + ts=parent_message["ts"], + text=parent_message["text"], + blocks=parent_message["blocks"], + metadata={ + "event_type": "assistant_thread_context", + "event_payload": context, + }, + ) + + def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: + parent_message = self._retrieve_first_bot_reply(channel_id, thread_ts) + if parent_message is not None and parent_message.get("metadata"): + if bool(parent_message["metadata"]["event_payload"]): + return AssistantThreadContext(parent_message["metadata"]["event_payload"]) + return None + + def _retrieve_first_bot_reply(self, channel_id: str, thread_ts: str) -> Optional[dict]: + messages: List[dict] = self.client.conversations_replies( + channel=channel_id, + ts=thread_ts, + oldest=thread_ts, + include_all_metadata=True, + limit=4, # 2 should be usually enough but buffer for more robustness + ).get("messages", []) + for message in messages: + if message.get("subtype") is None and message.get("user") == self.context.bot_user_id: + return message + return None diff --git a/slack_bolt/context/assistant/thread_context_store/file/__init__.py b/slack_bolt/context/assistant/thread_context_store/file/__init__.py new file mode 100644 index 000000000..a29f3b2c0 --- /dev/null +++ b/slack_bolt/context/assistant/thread_context_store/file/__init__.py @@ -0,0 +1,37 @@ +import json +from typing import Optional, Dict, Union +from pathlib import Path + +from ..store import AssistantThreadContextStore, AssistantThreadContext + + +class FileAssistantThreadContextStore(AssistantThreadContextStore): + + def __init__( + self, + base_dir: str = str(Path.home()) + "/.bolt-app-assistant-thread-contexts", + ): + self.base_dir = base_dir + self._mkdir(self.base_dir) + + def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: + path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" + with open(path, "w") as f: + f.write(json.dumps(context)) + + def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: + path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" + try: + with open(path) as f: + data = json.loads(f.read()) + if data.get("channel_id") is not None: + return AssistantThreadContext(data) + except FileNotFoundError: + pass + return None + + @staticmethod + def _mkdir(path: Union[str, Path]): + if isinstance(path, str): + path = Path(path) + path.mkdir(parents=True, exist_ok=True) diff --git a/slack_bolt/context/assistant/thread_context_store/store.py b/slack_bolt/context/assistant/thread_context_store/store.py new file mode 100644 index 000000000..2e29c55df --- /dev/null +++ b/slack_bolt/context/assistant/thread_context_store/store.py @@ -0,0 +1,11 @@ +from typing import Dict, Optional + +from slack_bolt.context.assistant.thread_context import AssistantThreadContext + + +class AssistantThreadContextStore: + def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: + raise NotImplementedError() + + def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: + raise NotImplementedError() diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index 58eba0850..47eb4744e 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -7,7 +7,12 @@ from slack_bolt.context.complete.async_complete import AsyncComplete from slack_bolt.context.fail.async_fail import AsyncFail from slack_bolt.context.respond.async_respond import AsyncRespond +from slack_bolt.context.get_thread_context.async_get_thread_context import AsyncGetThreadContext +from slack_bolt.context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext from slack_bolt.context.say.async_say import AsyncSay +from slack_bolt.context.set_status.async_set_status import AsyncSetStatus +from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts +from slack_bolt.context.set_title.async_set_title import AsyncSetTitle from slack_bolt.util.utils import create_copy @@ -105,7 +110,7 @@ async def handle_button_clicks(ack, say): Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"] @property @@ -181,3 +186,23 @@ async def handle_button_clicks(context): if "fail" not in self: self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] + + @property + def set_title(self) -> Optional[AsyncSetTitle]: + return self.get("set_title") + + @property + def set_status(self) -> Optional[AsyncSetStatus]: + return self.get("set_status") + + @property + def set_suggested_prompts(self) -> Optional[AsyncSetSuggestedPrompts]: + return self.get("set_suggested_prompts") + + @property + def get_thread_context(self) -> Optional[AsyncGetThreadContext]: + return self.get("get_thread_context") + + @property + def save_thread_context(self) -> Optional[AsyncSaveThreadContext]: + return self.get("save_thread_context") diff --git a/slack_bolt/context/base_context.py b/slack_bolt/context/base_context.py index 2c00d8082..843d5ef60 100644 --- a/slack_bolt/context/base_context.py +++ b/slack_bolt/context/base_context.py @@ -18,6 +18,7 @@ class BaseContext(dict): "actor_team_id", "actor_user_id", "channel_id", + "thread_ts", "response_url", "matches", "authorize_result", @@ -34,9 +35,18 @@ class BaseContext(dict): "respond", "complete", "fail", + "set_status", + "set_title", + "set_suggested_prompts", ] + # Note that these items are not copyable, so when you add new items to this list, + # you must modify ThreadListenerRunner/AsyncioListenerRunner's _build_lazy_request method to pass the values. + # Other listener runners do not require the change because they invoke a lazy listener over the network, + # meaning that the context initialization would be done again. non_copyable_standard_property_names = [ "listener_runner", + "get_thread_context", + "save_thread_context", ] standard_property_names = copyable_standard_property_names + non_copyable_standard_property_names @@ -100,6 +110,11 @@ def channel_id(self) -> Optional[str]: """The conversation ID associated with this request.""" return self.get("channel_id") + @property + def thread_ts(self) -> Optional[str]: + """The conversation thread's ID associated with this request.""" + return self.get("thread_ts") + @property def response_url(self) -> Optional[str]: """The `response_url` associated with this request.""" diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index c9194abb8..31edf2891 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -6,8 +6,13 @@ from slack_bolt.context.base_context import BaseContext from slack_bolt.context.complete import Complete from slack_bolt.context.fail import Fail +from slack_bolt.context.get_thread_context.get_thread_context import GetThreadContext from slack_bolt.context.respond import Respond +from slack_bolt.context.save_thread_context import SaveThreadContext from slack_bolt.context.say import Say +from slack_bolt.context.set_status import SetStatus +from slack_bolt.context.set_suggested_prompts import SetSuggestedPrompts +from slack_bolt.context.set_title import SetTitle from slack_bolt.util.utils import create_copy @@ -106,7 +111,7 @@ def handle_button_clicks(ack, say): Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id) + self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"] @property @@ -182,3 +187,23 @@ def handle_button_clicks(context): if "fail" not in self: self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] + + @property + def set_title(self) -> Optional[SetTitle]: + return self.get("set_title") + + @property + def set_status(self) -> Optional[SetStatus]: + return self.get("set_status") + + @property + def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: + return self.get("set_suggested_prompts") + + @property + def get_thread_context(self) -> Optional[GetThreadContext]: + return self.get("get_thread_context") + + @property + def save_thread_context(self) -> Optional[SaveThreadContext]: + return self.get("save_thread_context") diff --git a/slack_bolt/context/get_thread_context/__init__.py b/slack_bolt/context/get_thread_context/__init__.py new file mode 100644 index 000000000..dd99b1b20 --- /dev/null +++ b/slack_bolt/context/get_thread_context/__init__.py @@ -0,0 +1,6 @@ +# Don't add async module imports here +from .get_thread_context import GetThreadContext + +__all__ = [ + "GetThreadContext", +] diff --git a/slack_bolt/context/get_thread_context/async_get_thread_context.py b/slack_bolt/context/get_thread_context/async_get_thread_context.py new file mode 100644 index 000000000..cb8683a10 --- /dev/null +++ b/slack_bolt/context/get_thread_context/async_get_thread_context.py @@ -0,0 +1,48 @@ +from typing import Optional + +from slack_bolt.context.assistant.thread_context import AssistantThreadContext +from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore + + +class AsyncGetThreadContext: + thread_context_store: AsyncAssistantThreadContextStore + payload: dict + channel_id: str + thread_ts: str + + _thread_context: Optional[AssistantThreadContext] + thread_context_loaded: bool + + def __init__( + self, + thread_context_store: AsyncAssistantThreadContextStore, + channel_id: str, + thread_ts: str, + payload: dict, + ): + self.thread_context_store = thread_context_store + self.payload = payload + self.channel_id = channel_id + self.thread_ts = thread_ts + self._thread_context: Optional[AssistantThreadContext] = None + self.thread_context_loaded = False + + async def __call__(self) -> Optional[AssistantThreadContext]: + if self.thread_context_loaded is True: + return self._thread_context + + if self.payload.get("assistant_thread") is not None: + # assistant_thread_started + thread = self.payload["assistant_thread"] + self._thread_context = ( + AssistantThreadContext(thread["context"]) + if thread.get("context", {}).get("channel_id") is not None + else None + ) + # for this event, the context will never be changed + self.thread_context_loaded = True + elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: + # message event + self._thread_context = await self.thread_context_store.find(channel_id=self.channel_id, thread_ts=self.thread_ts) + + return self._thread_context diff --git a/slack_bolt/context/get_thread_context/get_thread_context.py b/slack_bolt/context/get_thread_context/get_thread_context.py new file mode 100644 index 000000000..0a77d2d9f --- /dev/null +++ b/slack_bolt/context/get_thread_context/get_thread_context.py @@ -0,0 +1,48 @@ +from typing import Optional + +from slack_bolt.context.assistant.thread_context import AssistantThreadContext +from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore + + +class GetThreadContext: + thread_context_store: AssistantThreadContextStore + payload: dict + channel_id: str + thread_ts: str + + _thread_context: Optional[AssistantThreadContext] + thread_context_loaded: bool + + def __init__( + self, + thread_context_store: AssistantThreadContextStore, + channel_id: str, + thread_ts: str, + payload: dict, + ): + self.thread_context_store = thread_context_store + self.payload = payload + self.channel_id = channel_id + self.thread_ts = thread_ts + self._thread_context: Optional[AssistantThreadContext] = None + self.thread_context_loaded = False + + def __call__(self) -> Optional[AssistantThreadContext]: + if self.thread_context_loaded is True: + return self._thread_context + + if self.payload.get("assistant_thread") is not None: + # assistant_thread_started + thread = self.payload["assistant_thread"] + self._thread_context = ( + AssistantThreadContext(thread["context"]) + if thread.get("context", {}).get("channel_id") is not None + else None + ) + # for this event, the context will never be changed + self.thread_context_loaded = True + elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: + # message event + self._thread_context = self.thread_context_store.find(channel_id=self.channel_id, thread_ts=self.thread_ts) + + return self._thread_context diff --git a/slack_bolt/context/save_thread_context/__init__.py b/slack_bolt/context/save_thread_context/__init__.py new file mode 100644 index 000000000..4980e0830 --- /dev/null +++ b/slack_bolt/context/save_thread_context/__init__.py @@ -0,0 +1,6 @@ +# Don't add async module imports here +from .save_thread_context import SaveThreadContext + +__all__ = [ + "SaveThreadContext", +] diff --git a/slack_bolt/context/save_thread_context/async_save_thread_context.py b/slack_bolt/context/save_thread_context/async_save_thread_context.py new file mode 100644 index 000000000..ff79f5f64 --- /dev/null +++ b/slack_bolt/context/save_thread_context/async_save_thread_context.py @@ -0,0 +1,26 @@ +from typing import Dict + +from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore + + +class AsyncSaveThreadContext: + thread_context_store: AsyncAssistantThreadContextStore + channel_id: str + thread_ts: str + + def __init__( + self, + thread_context_store: AsyncAssistantThreadContextStore, + channel_id: str, + thread_ts: str, + ): + self.thread_context_store = thread_context_store + self.channel_id = channel_id + self.thread_ts = thread_ts + + async def __call__(self, new_context: Dict[str, str]) -> None: + await self.thread_context_store.save( + channel_id=self.channel_id, + thread_ts=self.thread_ts, + context=new_context, + ) diff --git a/slack_bolt/context/save_thread_context/save_thread_context.py b/slack_bolt/context/save_thread_context/save_thread_context.py new file mode 100644 index 000000000..4d0a13dfd --- /dev/null +++ b/slack_bolt/context/save_thread_context/save_thread_context.py @@ -0,0 +1,26 @@ +from typing import Dict + +from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore + + +class SaveThreadContext: + thread_context_store: AssistantThreadContextStore + channel_id: str + thread_ts: str + + def __init__( + self, + thread_context_store: AssistantThreadContextStore, + channel_id: str, + thread_ts: str, + ): + self.thread_context_store = thread_context_store + self.channel_id = channel_id + self.thread_ts = thread_ts + + def __call__(self, new_context: Dict[str, str]) -> None: + self.thread_context_store.save( + channel_id=self.channel_id, + thread_ts=self.thread_ts, + context=new_context, + ) diff --git a/slack_bolt/context/say/async_say.py b/slack_bolt/context/say/async_say.py index 855776cbe..b771529b0 100644 --- a/slack_bolt/context/say/async_say.py +++ b/slack_bolt/context/say/async_say.py @@ -1,4 +1,4 @@ -from typing import Optional, Union, Dict, Sequence +from typing import Optional, Union, Dict, Sequence, Callable, Awaitable from slack_sdk.models.metadata import Metadata @@ -13,14 +13,20 @@ class AsyncSay: client: Optional[AsyncWebClient] channel: Optional[str] + thread_ts: Optional[str] + build_metadata: Optional[Callable[[], Awaitable[Union[Dict, Metadata]]]] def __init__( self, client: Optional[AsyncWebClient], channel: Optional[str], + thread_ts: Optional[str] = None, + build_metadata: Optional[Callable[[], Awaitable[Union[Dict, Metadata]]]] = None, ): self.client = client self.channel = channel + self.thread_ts = thread_ts + self.build_metadata = build_metadata async def __call__( self, @@ -43,6 +49,8 @@ async def __call__( **kwargs, ) -> AsyncSlackResponse: if _can_say(self, channel): + if metadata is None and self.build_metadata is not None: + metadata = await self.build_metadata() text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response @@ -52,7 +60,7 @@ async def __call__( blocks=blocks, attachments=attachments, as_user=as_user, - thread_ts=thread_ts, + thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, @@ -69,6 +77,10 @@ async def __call__( message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel + if "thread_ts" not in message: + message["thread_ts"] = thread_ts or self.thread_ts + if "metadata" not in message: + message["metadata"] = metadata return await self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") diff --git a/slack_bolt/context/say/say.py b/slack_bolt/context/say/say.py index f6ecd337c..6c0127a62 100644 --- a/slack_bolt/context/say/say.py +++ b/slack_bolt/context/say/say.py @@ -13,14 +13,20 @@ class Say: client: Optional[WebClient] channel: Optional[str] + thread_ts: Optional[str] + metadata: Optional[Union[Dict, Metadata]] def __init__( self, client: Optional[WebClient], channel: Optional[str], + thread_ts: Optional[str] = None, + metadata: Optional[Union[Dict, Metadata]] = None, ): self.client = client self.channel = channel + self.thread_ts = thread_ts + self.metadata = metadata def __call__( self, @@ -52,7 +58,7 @@ def __call__( blocks=blocks, attachments=attachments, as_user=as_user, - thread_ts=thread_ts, + thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, @@ -62,13 +68,17 @@ def __call__( mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata, + metadata=metadata or self.metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel + if "thread_ts" not in message: + message["thread_ts"] = thread_ts or self.thread_ts + if "metadata" not in message: + message["metadata"] = metadata or self.metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") diff --git a/slack_bolt/context/set_status/__init__.py b/slack_bolt/context/set_status/__init__.py new file mode 100644 index 000000000..c12f9658b --- /dev/null +++ b/slack_bolt/context/set_status/__init__.py @@ -0,0 +1,6 @@ +# Don't add async module imports here +from .set_status import SetStatus + +__all__ = [ + "SetStatus", +] diff --git a/slack_bolt/context/set_status/async_set_status.py b/slack_bolt/context/set_status/async_set_status.py new file mode 100644 index 000000000..926ec6de8 --- /dev/null +++ b/slack_bolt/context/set_status/async_set_status.py @@ -0,0 +1,25 @@ +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_slack_response import AsyncSlackResponse + + +class AsyncSetStatus: + client: AsyncWebClient + channel_id: str + thread_ts: str + + def __init__( + self, + client: AsyncWebClient, + channel_id: str, + thread_ts: str, + ): + self.client = client + self.channel_id = channel_id + self.thread_ts = thread_ts + + async def __call__(self, status: str) -> AsyncSlackResponse: + return await self.client.assistant_threads_setStatus( + status=status, + channel_id=self.channel_id, + thread_ts=self.thread_ts, + ) diff --git a/slack_bolt/context/set_status/set_status.py b/slack_bolt/context/set_status/set_status.py new file mode 100644 index 000000000..8df0d49a7 --- /dev/null +++ b/slack_bolt/context/set_status/set_status.py @@ -0,0 +1,25 @@ +from slack_sdk import WebClient +from slack_sdk.web import SlackResponse + + +class SetStatus: + client: WebClient + channel_id: str + thread_ts: str + + def __init__( + self, + client: WebClient, + channel_id: str, + thread_ts: str, + ): + self.client = client + self.channel_id = channel_id + self.thread_ts = thread_ts + + def __call__(self, status: str) -> SlackResponse: + return self.client.assistant_threads_setStatus( + status=status, + channel_id=self.channel_id, + thread_ts=self.thread_ts, + ) diff --git a/slack_bolt/context/set_suggested_prompts/__init__.py b/slack_bolt/context/set_suggested_prompts/__init__.py new file mode 100644 index 000000000..e5efd26c7 --- /dev/null +++ b/slack_bolt/context/set_suggested_prompts/__init__.py @@ -0,0 +1,6 @@ +# Don't add async module imports here +from .set_suggested_prompts import SetSuggestedPrompts + +__all__ = [ + "SetSuggestedPrompts", +] diff --git a/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py new file mode 100644 index 000000000..76f827732 --- /dev/null +++ b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py @@ -0,0 +1,34 @@ +from typing import List, Dict, Union + +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_slack_response import AsyncSlackResponse + + +class AsyncSetSuggestedPrompts: + client: AsyncWebClient + channel_id: str + thread_ts: str + + def __init__( + self, + client: AsyncWebClient, + channel_id: str, + thread_ts: str, + ): + self.client = client + self.channel_id = channel_id + self.thread_ts = thread_ts + + async def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> AsyncSlackResponse: + prompts_arg: List[Dict[str, str]] = [] + for prompt in prompts: + if isinstance(prompt, str): + prompts_arg.append({"title": prompt, "message": prompt}) + else: + prompts_arg.append(prompt) + + return await self.client.assistant_threads_setSuggestedPrompts( + channel_id=self.channel_id, + thread_ts=self.thread_ts, + prompts=prompts_arg, + ) diff --git a/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py new file mode 100644 index 000000000..3714f4830 --- /dev/null +++ b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py @@ -0,0 +1,34 @@ +from typing import List, Dict, Union + +from slack_sdk import WebClient +from slack_sdk.web import SlackResponse + + +class SetSuggestedPrompts: + client: WebClient + channel_id: str + thread_ts: str + + def __init__( + self, + client: WebClient, + channel_id: str, + thread_ts: str, + ): + self.client = client + self.channel_id = channel_id + self.thread_ts = thread_ts + + def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse: + prompts_arg: List[Dict[str, str]] = [] + for prompt in prompts: + if isinstance(prompt, str): + prompts_arg.append({"title": prompt, "message": prompt}) + else: + prompts_arg.append(prompt) + + return self.client.assistant_threads_setSuggestedPrompts( + channel_id=self.channel_id, + thread_ts=self.thread_ts, + prompts=prompts_arg, + ) diff --git a/slack_bolt/context/set_title/__init__.py b/slack_bolt/context/set_title/__init__.py new file mode 100644 index 000000000..e799e88ae --- /dev/null +++ b/slack_bolt/context/set_title/__init__.py @@ -0,0 +1,6 @@ +# Don't add async module imports here +from .set_title import SetTitle + +__all__ = [ + "SetTitle", +] diff --git a/slack_bolt/context/set_title/async_set_title.py b/slack_bolt/context/set_title/async_set_title.py new file mode 100644 index 000000000..ea6bfc98a --- /dev/null +++ b/slack_bolt/context/set_title/async_set_title.py @@ -0,0 +1,25 @@ +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_slack_response import AsyncSlackResponse + + +class AsyncSetTitle: + client: AsyncWebClient + channel_id: str + thread_ts: str + + def __init__( + self, + client: AsyncWebClient, + channel_id: str, + thread_ts: str, + ): + self.client = client + self.channel_id = channel_id + self.thread_ts = thread_ts + + async def __call__(self, title: str) -> AsyncSlackResponse: + return await self.client.assistant_threads_setTitle( + title=title, + channel_id=self.channel_id, + thread_ts=self.thread_ts, + ) diff --git a/slack_bolt/context/set_title/set_title.py b/slack_bolt/context/set_title/set_title.py new file mode 100644 index 000000000..5670c6b73 --- /dev/null +++ b/slack_bolt/context/set_title/set_title.py @@ -0,0 +1,25 @@ +from slack_sdk import WebClient +from slack_sdk.web import SlackResponse + + +class SetTitle: + client: WebClient + channel_id: str + thread_ts: str + + def __init__( + self, + client: WebClient, + channel_id: str, + thread_ts: str, + ): + self.client = client + self.channel_id = channel_id + self.thread_ts = thread_ts + + def __call__(self, title: str) -> SlackResponse: + return self.client.assistant_threads_setTitle( + title=title, + channel_id=self.channel_id, + thread_ts=self.thread_ts, + ) diff --git a/slack_bolt/kwargs_injection/args.py b/slack_bolt/kwargs_injection/args.py index 68e64a8e8..1a0ec3ca8 100644 --- a/slack_bolt/kwargs_injection/args.py +++ b/slack_bolt/kwargs_injection/args.py @@ -6,8 +6,13 @@ from slack_bolt.context.ack import Ack from slack_bolt.context.complete import Complete from slack_bolt.context.fail import Fail +from slack_bolt.context.get_thread_context.get_thread_context import GetThreadContext from slack_bolt.context.respond import Respond +from slack_bolt.context.save_thread_context import SaveThreadContext from slack_bolt.context.say import Say +from slack_bolt.context.set_status import SetStatus +from slack_bolt.context.set_suggested_prompts import SetSuggestedPrompts +from slack_bolt.context.set_title import SetTitle from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse from slack_sdk import WebClient @@ -87,6 +92,16 @@ def handle_buttons(args): """`complete()` utility function, signals a successful completion of the custom function""" fail: Fail """`fail()` utility function, signal that the custom function failed to complete""" + set_status: Optional[SetStatus] + """`set_status()` utility function for AI Agents & Assistants""" + set_title: Optional[SetTitle] + """`set_title()` utility function for AI Agents & Assistants""" + set_suggested_prompts: Optional[SetSuggestedPrompts] + """`set_suggested_prompts()` utility function for AI Agents & Assistants""" + get_thread_context: Optional[GetThreadContext] + """`get_thread_context()` utility function for AI Agents & Assistants""" + save_thread_context: Optional[SaveThreadContext] + """`save_thread_context()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -115,6 +130,11 @@ def __init__( respond: Respond, complete: Complete, fail: Fail, + set_status: Optional[SetStatus] = None, + set_title: Optional[SetTitle] = None, + set_suggested_prompts: Optional[SetSuggestedPrompts] = None, + get_thread_context: Optional[GetThreadContext] = None, + save_thread_context: Optional[SaveThreadContext] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method @@ -142,5 +162,12 @@ def __init__( self.respond: Respond = respond self.complete: Complete = complete self.fail: Fail = fail + + self.set_status = set_status + self.set_title = set_title + self.set_suggested_prompts = set_suggested_prompts + self.get_thread_context = get_thread_context + self.save_thread_context = save_thread_context + self.next: Callable[[], None] = next self.next_: Callable[[], None] = next diff --git a/slack_bolt/kwargs_injection/async_args.py b/slack_bolt/kwargs_injection/async_args.py index 1601a552a..4953f2167 100644 --- a/slack_bolt/kwargs_injection/async_args.py +++ b/slack_bolt/kwargs_injection/async_args.py @@ -6,7 +6,12 @@ from slack_bolt.context.complete.async_complete import AsyncComplete from slack_bolt.context.fail.async_fail import AsyncFail from slack_bolt.context.respond.async_respond import AsyncRespond +from slack_bolt.context.get_thread_context.async_get_thread_context import AsyncGetThreadContext +from slack_bolt.context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext from slack_bolt.context.say.async_say import AsyncSay +from slack_bolt.context.set_status.async_set_status import AsyncSetStatus +from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts +from slack_bolt.context.set_title.async_set_title import AsyncSetTitle from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse from slack_sdk.web.async_client import AsyncWebClient @@ -86,6 +91,16 @@ async def handle_buttons(args): """`complete()` utility function, signals a successful completion of the custom function""" fail: AsyncFail """`fail()` utility function, signal that the custom function failed to complete""" + set_status: Optional[AsyncSetStatus] + """`set_status()` utility function for AI Agents & Assistants""" + set_title: Optional[AsyncSetTitle] + """`set_title()` utility function for AI Agents & Assistants""" + set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] + """`set_suggested_prompts()` utility function for AI Agents & Assistants""" + get_thread_context: Optional[AsyncGetThreadContext] + """`get_thread_context()` utility function for AI Agents & Assistants""" + save_thread_context: Optional[AsyncSaveThreadContext] + """`save_thread_context()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], Awaitable[None]] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -114,6 +129,11 @@ def __init__( respond: AsyncRespond, complete: AsyncComplete, fail: AsyncFail, + set_status: Optional[AsyncSetStatus] = None, + set_title: Optional[AsyncSetTitle] = None, + set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] = None, + get_thread_context: Optional[AsyncGetThreadContext] = None, + save_thread_context: Optional[AsyncSaveThreadContext] = None, next: Callable[[], Awaitable[None]], **kwargs, # noqa ): @@ -138,5 +158,12 @@ def __init__( self.respond: AsyncRespond = respond self.complete: AsyncComplete = complete self.fail: AsyncFail = fail + + self.set_status = set_status + self.set_title = set_title + self.set_suggested_prompts = set_suggested_prompts + self.get_thread_context = get_thread_context + self.save_thread_context = save_thread_context + self.next: Callable[[], Awaitable[None]] = next self.next_: Callable[[], Awaitable[None]] = next diff --git a/slack_bolt/kwargs_injection/async_utils.py b/slack_bolt/kwargs_injection/async_utils.py index a31b079db..c8870c3cc 100644 --- a/slack_bolt/kwargs_injection/async_utils.py +++ b/slack_bolt/kwargs_injection/async_utils.py @@ -53,6 +53,11 @@ def build_async_required_kwargs( "respond": request.context.respond, "complete": request.context.complete, "fail": request.context.fail, + "set_status": request.context.set_status, + "set_title": request.context.set_title, + "set_suggested_prompts": request.context.set_suggested_prompts, + "get_thread_context": request.context.get_thread_context, + "save_thread_context": request.context.save_thread_context, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function diff --git a/slack_bolt/kwargs_injection/utils.py b/slack_bolt/kwargs_injection/utils.py index 30b5d21e2..c1909c67a 100644 --- a/slack_bolt/kwargs_injection/utils.py +++ b/slack_bolt/kwargs_injection/utils.py @@ -53,6 +53,10 @@ def build_required_kwargs( "respond": request.context.respond, "complete": request.context.complete, "fail": request.context.fail, + "set_status": request.context.set_status, + "set_title": request.context.set_title, + "set_suggested_prompts": request.context.set_suggested_prompts, + "save_thread_context": request.context.save_thread_context, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function diff --git a/slack_bolt/listener/asyncio_runner.py b/slack_bolt/listener/asyncio_runner.py index 01e8641ed..56dc29cc1 100644 --- a/slack_bolt/listener/asyncio_runner.py +++ b/slack_bolt/listener/asyncio_runner.py @@ -179,6 +179,10 @@ def _build_lazy_request(self, request: AsyncBoltRequest, lazy_func_name: str) -> copied_request.lazy_only = True copied_request.lazy_function_name = lazy_func_name copied_request.context["listener_runner"] = self + if request.context.get_thread_context is not None: + copied_request.context["get_thread_context"] = request.context.get_thread_context + if request.context.save_thread_context is not None: + copied_request.context["save_thread_context"] = request.context.save_thread_context return copied_request def _debug_log_completion(self, starting_time: float, response: BoltResponse) -> None: diff --git a/slack_bolt/listener/thread_runner.py b/slack_bolt/listener/thread_runner.py index c2d87b3d5..0b79c6ffd 100644 --- a/slack_bolt/listener/thread_runner.py +++ b/slack_bolt/listener/thread_runner.py @@ -189,7 +189,12 @@ def _build_lazy_request(self, request: BoltRequest, lazy_func_name: str) -> Bolt copied_request: BoltRequest = create_copy(request.to_copyable()) copied_request.lazy_only = True copied_request.lazy_function_name = lazy_func_name + # These are not copyable objects, so manually set for a different thread copied_request.context["listener_runner"] = self + if request.context.get_thread_context is not None: + copied_request.context["get_thread_context"] = request.context.get_thread_context + if request.context.save_thread_context is not None: + copied_request.context["save_thread_context"] = request.context.save_thread_context return copied_request def _debug_log_completion(self, starting_time: float, response: BoltResponse) -> None: diff --git a/slack_bolt/middleware/__init__.py b/slack_bolt/middleware/__init__.py index ee962146f..0e4044f99 100644 --- a/slack_bolt/middleware/__init__.py +++ b/slack_bolt/middleware/__init__.py @@ -26,6 +26,7 @@ IgnoringSelfEvents, UrlVerification, AttachingFunctionToken, + # Assistant, # to avoid circular imports ] for cls in builtin_middleware_classes: Middleware.register(cls) # type: ignore[arg-type] diff --git a/slack_bolt/middleware/assistant/__init__.py b/slack_bolt/middleware/assistant/__init__.py new file mode 100644 index 000000000..4487394ab --- /dev/null +++ b/slack_bolt/middleware/assistant/__init__.py @@ -0,0 +1,6 @@ +# Don't add async module imports here +from .assistant import Assistant + +__all__ = [ + "Assistant", +] diff --git a/slack_bolt/middleware/assistant/assistant.py b/slack_bolt/middleware/assistant/assistant.py new file mode 100644 index 000000000..beac71bca --- /dev/null +++ b/slack_bolt/middleware/assistant/assistant.py @@ -0,0 +1,291 @@ +import logging +from functools import wraps +from logging import Logger +from typing import List, Optional, Union, Callable + +from slack_bolt.context.save_thread_context import SaveThreadContext +from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore +from slack_bolt.listener_matcher.builtins import build_listener_matcher + +from slack_bolt.request.request import BoltRequest +from slack_bolt.response.response import BoltResponse +from slack_bolt.listener_matcher import CustomListenerMatcher +from slack_bolt.error import BoltError +from slack_bolt.listener.custom_listener import CustomListener +from slack_bolt.listener import Listener +from slack_bolt.listener.thread_runner import ThreadListenerRunner +from slack_bolt.middleware import Middleware +from slack_bolt.listener_matcher import ListenerMatcher +from slack_bolt.request.payload_utils import ( + is_assistant_thread_started_event, + is_user_message_event_in_assistant_thread, + is_assistant_thread_context_changed_event, + is_other_message_sub_event_in_assistant_thread, + is_bot_message_event_in_assistant_thread, +) +from slack_bolt.util.utils import is_used_without_argument + + +class Assistant(Middleware): + _thread_started_listeners: Optional[List[Listener]] + _thread_context_changed_listeners: Optional[List[Listener]] + _user_message_listeners: Optional[List[Listener]] + _bot_message_listeners: Optional[List[Listener]] + + thread_context_store: Optional[AssistantThreadContextStore] + base_logger: Optional[logging.Logger] + + def __init__( + self, + *, + app_name: str = "assistant", + thread_context_store: Optional[AssistantThreadContextStore] = None, + logger: Optional[logging.Logger] = None, + ): + self.app_name = app_name + self.thread_context_store = thread_context_store + self.base_logger = logger + + self._thread_started_listeners = None + self._thread_context_changed_listeners = None + self._user_message_listeners = None + self._bot_message_listeners = None + + def thread_started( + self, + *args, + matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, + middleware: Optional[Union[Callable, Middleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._thread_started_listeners is None: + self._thread_started_listeners = [] + all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers) + if is_used_without_argument(args): + func = args[0] + self._thread_started_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._thread_started_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + def user_message( + self, + *args, + matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, + middleware: Optional[Union[Callable, Middleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._user_message_listeners is None: + self._user_message_listeners = [] + all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers) + if is_used_without_argument(args): + func = args[0] + self._user_message_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._user_message_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + def bot_message( + self, + *args, + matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, + middleware: Optional[Union[Callable, Middleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._bot_message_listeners is None: + self._bot_message_listeners = [] + all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers) + if is_used_without_argument(args): + func = args[0] + self._bot_message_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._bot_message_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + def thread_context_changed( + self, + *args, + matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, + middleware: Optional[Union[Callable, Middleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._thread_context_changed_listeners is None: + self._thread_context_changed_listeners = [] + all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers) + if is_used_without_argument(args): + func = args[0] + self._thread_context_changed_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._thread_context_changed_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + def _merge_matchers( + self, + primary_matcher: Callable[..., bool], + custom_matchers: Optional[Union[Callable[..., bool], ListenerMatcher]], + ): + return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + ( + custom_matchers or [] + ) # type:ignore[operator] + + @staticmethod + def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): + save_thread_context(payload["assistant_thread"]["context"]) + + def process( # type:ignore[return] + self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse] + ) -> Optional[BoltResponse]: + if self._thread_context_changed_listeners is None: + self.thread_context_changed(self.default_thread_context_changed) + + listener_runner: ThreadListenerRunner = req.context.listener_runner + for listeners in [ + self._thread_started_listeners, + self._thread_context_changed_listeners, + self._user_message_listeners, + self._bot_message_listeners, + ]: + if listeners is not None: + for listener in listeners: + if listener.matches(req=req, resp=resp): + return listener_runner.run( + request=req, + response=resp, + listener_name="assistant_listener", + listener=listener, + ) + if is_other_message_sub_event_in_assistant_thread(req.body): + # message_changed, message_deleted, etc. + return req.context.ack() + + next() + + def build_listener( + self, + listener_or_functions: Union[Listener, Callable, List[Callable]], + matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, + middleware: Optional[List[Middleware]] = None, + base_logger: Optional[Logger] = None, + ) -> Listener: + if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] + listener_or_functions = [listener_or_functions] # type:ignore[list-item] + + if isinstance(listener_or_functions, Listener): + return listener_or_functions + elif isinstance(listener_or_functions, list): + middleware = middleware if middleware else [] + functions = listener_or_functions + ack_function = functions.pop(0) + + matchers = matchers if matchers else [] + listener_matchers: List[ListenerMatcher] = [] + for matcher in matchers: + if isinstance(matcher, ListenerMatcher): + listener_matchers.append(matcher) + elif isinstance(matcher, Callable): # type:ignore[arg-type] + listener_matchers.append( + build_listener_matcher( + func=matcher, + asyncio=False, + base_logger=base_logger, + ) + ) + return CustomListener( + app_name=self.app_name, + matchers=listener_matchers, + middleware=middleware, + ack_function=ack_function, + lazy_functions=functions, + auto_acknowledgement=True, + base_logger=base_logger or self.base_logger, + ) + else: + raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected") diff --git a/slack_bolt/middleware/assistant/async_assistant.py b/slack_bolt/middleware/assistant/async_assistant.py new file mode 100644 index 000000000..2fdd828d7 --- /dev/null +++ b/slack_bolt/middleware/assistant/async_assistant.py @@ -0,0 +1,320 @@ +import logging +from functools import wraps +from logging import Logger +from typing import List, Optional, Union, Callable, Awaitable + +from slack_bolt.context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext +from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore + +from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner +from slack_bolt.listener_matcher.builtins import build_listener_matcher +from slack_bolt.request.async_request import AsyncBoltRequest +from slack_bolt.response import BoltResponse +from slack_bolt.error import BoltError +from slack_bolt.listener.async_listener import AsyncListener, AsyncCustomListener +from slack_bolt.middleware.async_middleware import AsyncMiddleware +from slack_bolt.listener_matcher.async_listener_matcher import AsyncListenerMatcher +from slack_bolt.request.payload_utils import ( + is_assistant_thread_started_event, + is_user_message_event_in_assistant_thread, + is_assistant_thread_context_changed_event, + is_other_message_sub_event_in_assistant_thread, + is_bot_message_event_in_assistant_thread, +) +from slack_bolt.util.utils import is_used_without_argument + + +class AsyncAssistant(AsyncMiddleware): + _thread_started_listeners: Optional[List[AsyncListener]] + _user_message_listeners: Optional[List[AsyncListener]] + _bot_message_listeners: Optional[List[AsyncListener]] + _thread_context_changed_listeners: Optional[List[AsyncListener]] + + thread_context_store: Optional[AsyncAssistantThreadContextStore] + base_logger: Optional[logging.Logger] + + def __init__( + self, + *, + app_name: str = "assistant", + thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, + logger: Optional[logging.Logger] = None, + ): + self.app_name = app_name + self.thread_context_store = thread_context_store + self.base_logger = logger + + self._thread_started_listeners = None + self._thread_context_changed_listeners = None + self._user_message_listeners = None + self._bot_message_listeners = None + + def thread_started( + self, + *args, + matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None, + middleware: Optional[Union[Callable, AsyncMiddleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._thread_started_listeners is None: + self._thread_started_listeners = [] + all_matchers = self._merge_matchers( + build_listener_matcher( + func=is_assistant_thread_started_event, + asyncio=True, + base_logger=self.base_logger, + ), # type:ignore[arg-type] + matchers, + ) + if is_used_without_argument(args): + func = args[0] + self._thread_started_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._thread_started_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + def user_message( + self, + *args, + matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None, + middleware: Optional[Union[Callable, AsyncMiddleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._user_message_listeners is None: + self._user_message_listeners = [] + all_matchers = self._merge_matchers( + build_listener_matcher( + func=is_user_message_event_in_assistant_thread, + asyncio=True, + base_logger=self.base_logger, + ), # type:ignore[arg-type] + matchers, + ) + if is_used_without_argument(args): + func = args[0] + self._user_message_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._user_message_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + def bot_message( + self, + *args, + matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None, + middleware: Optional[Union[Callable, AsyncMiddleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._bot_message_listeners is None: + self._bot_message_listeners = [] + all_matchers = self._merge_matchers( + build_listener_matcher( + func=is_bot_message_event_in_assistant_thread, + asyncio=True, + base_logger=self.base_logger, + ), # type:ignore[arg-type] + matchers, + ) + if is_used_without_argument(args): + func = args[0] + self._bot_message_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._bot_message_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + def thread_context_changed( + self, + *args, + matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None, + middleware: Optional[Union[Callable, AsyncMiddleware]] = None, + lazy: Optional[List[Callable[..., None]]] = None, + ): + if self._thread_context_changed_listeners is None: + self._thread_context_changed_listeners = [] + all_matchers = self._merge_matchers( + build_listener_matcher( + func=is_assistant_thread_context_changed_event, + asyncio=True, + base_logger=self.base_logger, + ), # type:ignore[arg-type] + matchers, + ) + if is_used_without_argument(args): + func = args[0] + self._thread_context_changed_listeners.append( + self.build_listener( + listener_or_functions=func, + matchers=all_matchers, + middleware=middleware, # type:ignore[arg-type] + ) + ) + return func + + def _inner(func): + functions = [func] + (lazy if lazy is not None else []) + self._thread_context_changed_listeners.append( + self.build_listener( + listener_or_functions=functions, + matchers=all_matchers, + middleware=middleware, + ) + ) + + @wraps(func) + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return _wrapper + + return _inner + + @staticmethod + def _merge_matchers( + primary_matcher: Union[Callable[..., bool], AsyncListenerMatcher], + custom_matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]], + ): + return [primary_matcher] + (custom_matchers or []) # type:ignore[operator] + + @staticmethod + async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict): + new_context: dict = payload["assistant_thread"]["context"] + await save_thread_context(new_context) + + async def async_process( # type:ignore[return] + self, + *, + req: AsyncBoltRequest, + resp: BoltResponse, + next: Callable[[], Awaitable[BoltResponse]], + ) -> Optional[BoltResponse]: + if self._thread_context_changed_listeners is None: + self.thread_context_changed(self.default_thread_context_changed) + + listener_runner: AsyncioListenerRunner = req.context.listener_runner + for listeners in [ + self._thread_started_listeners, + self._thread_context_changed_listeners, + self._user_message_listeners, + self._bot_message_listeners, + ]: + if listeners is not None: + for listener in listeners: + if listener is not None and await listener.async_matches(req=req, resp=resp): + return await listener_runner.run( + request=req, + response=resp, + listener_name="assistant_listener", + listener=listener, + ) + if is_other_message_sub_event_in_assistant_thread(req.body): + # message_changed, message_deleted, etc. + return await req.context.ack() + + await next() + + def build_listener( + self, + listener_or_functions: Union[AsyncListener, Callable, List[Callable]], + matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]] = None, + middleware: Optional[List[AsyncMiddleware]] = None, + base_logger: Optional[Logger] = None, + ) -> AsyncListener: + if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] + listener_or_functions = [listener_or_functions] # type:ignore[list-item] + + if isinstance(listener_or_functions, AsyncListener): + return listener_or_functions + elif isinstance(listener_or_functions, list): + middleware = middleware if middleware else [] + functions = listener_or_functions + ack_function = functions.pop(0) + + matchers = matchers if matchers else [] + listener_matchers: List[AsyncListenerMatcher] = [] + for matcher in matchers: + if isinstance(matcher, AsyncListenerMatcher): + listener_matchers.append(matcher) + else: + listener_matchers.append( + build_listener_matcher( + func=matcher, # type:ignore[arg-type] + asyncio=True, + base_logger=base_logger, + ) + ) + return AsyncCustomListener( + app_name=self.app_name, + matchers=listener_matchers, + middleware=middleware, + ack_function=ack_function, + lazy_functions=functions, + auto_acknowledgement=True, + base_logger=base_logger or self.base_logger, + ) + else: + raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected") diff --git a/slack_bolt/middleware/authorization/single_team_authorization.py b/slack_bolt/middleware/authorization/single_team_authorization.py index 80a864b4e..c2bc1488c 100644 --- a/slack_bolt/middleware/authorization/single_team_authorization.py +++ b/slack_bolt/middleware/authorization/single_team_authorization.py @@ -47,6 +47,7 @@ def process( # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: + if _is_no_auth_required(req): return next() diff --git a/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.py b/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.py index ca2d7fed3..11a3f40ee 100644 --- a/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.py +++ b/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.py @@ -4,6 +4,7 @@ from slack_bolt.response import BoltResponse from .ignoring_self_events import IgnoringSelfEvents from slack_bolt.middleware.async_middleware import AsyncMiddleware +from slack_bolt.request.payload_utils import is_bot_message_event_in_assistant_thread class AsyncIgnoringSelfEvents(IgnoringSelfEvents, AsyncMiddleware): @@ -18,6 +19,11 @@ async def async_process( # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] + if self.ignoring_self_assistant_message_events_enabled is False: + if is_bot_message_event_in_assistant_thread(req.body): + # Assistant#bot_message handler acknowledges this pattern + return await next() + self._debug_log(req.body) return await req.context.ack() else: diff --git a/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.py b/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.py index 0870991e3..3380636f0 100644 --- a/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.py +++ b/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.py @@ -4,14 +4,20 @@ from slack_bolt.authorization import AuthorizeResult from slack_bolt.logger import get_bolt_logger from slack_bolt.request import BoltRequest +from slack_bolt.request.payload_utils import is_bot_message_event_in_assistant_thread from slack_bolt.response import BoltResponse from slack_bolt.middleware.middleware import Middleware class IgnoringSelfEvents(Middleware): - def __init__(self, base_logger: Optional[logging.Logger] = None): + def __init__( + self, + base_logger: Optional[logging.Logger] = None, + ignoring_self_assistant_message_events_enabled: bool = True, + ): """Ignores the events generated by this bot user itself.""" self.logger = get_bolt_logger(IgnoringSelfEvents, base_logger=base_logger) + self.ignoring_self_assistant_message_events_enabled = ignoring_self_assistant_message_events_enabled def process( self, @@ -24,6 +30,11 @@ def process( # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] + if self.ignoring_self_assistant_message_events_enabled is False: + if is_bot_message_event_in_assistant_thread(req.body): + # Assistant#bot_message handler acknowledges this pattern + return next() + self._debug_log(req.body) return req.context.ack() else: diff --git a/slack_bolt/request/async_internals.py b/slack_bolt/request/async_internals.py index f1f00dece..ea94739e8 100644 --- a/slack_bolt/request/async_internals.py +++ b/slack_bolt/request/async_internals.py @@ -14,6 +14,7 @@ extract_actor_enterprise_id, extract_actor_team_id, extract_actor_user_id, + extract_thread_ts, ) @@ -44,6 +45,9 @@ def build_async_context( channel_id = extract_channel_id(body) if channel_id: context["channel_id"] = channel_id + thread_ts = extract_thread_ts(body) + if thread_ts: + context["thread_ts"] = thread_ts function_execution_id = extract_function_execution_id(body) if function_execution_id: context["function_execution_id"] = function_execution_id diff --git a/slack_bolt/request/internals.py b/slack_bolt/request/internals.py index bee746bf2..b04f336bf 100644 --- a/slack_bolt/request/internals.py +++ b/slack_bolt/request/internals.py @@ -3,6 +3,7 @@ from urllib.parse import parse_qsl, parse_qs from slack_bolt.context import BoltContext +from slack_bolt.request.payload_utils import is_assistant_event def parse_query(query: Optional[Union[str, Dict[str, str], Dict[str, Sequence[str]]]]) -> Dict[str, Sequence[str]]: @@ -207,6 +208,31 @@ def extract_channel_id(payload: Dict[str, Any]) -> Optional[str]: if payload.get("item") is not None: # reaction_added: body["event"]["item"] return extract_channel_id(payload["item"]) + if payload.get("assistant_thread") is not None: + # assistant_thread_started + return extract_channel_id(payload["assistant_thread"]) + return None + + +def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]: + # This utility initially supports only the use cases for AI assistants, but it may be fine to add more patterns. + # That said, note that thread_ts is always required for assistant threads, but it's not for channels. + # Thus, blindly setting this thread_ts to say utility can break existing apps' behaviors. + if is_assistant_event(payload): + event = payload["event"] + if event.get("assistant_thread") is not None: + # assistant_thread_started, assistant_thread_context_changed + return event["assistant_thread"]["thread_ts"] + elif event.get("channel") is not None: + if event.get("thread_ts") is not None: + # message in an assistant thread + return event["thread_ts"] + elif event.get("message", {}).get("thread_ts") is not None: + # message_changed + return event["message"]["thread_ts"] + elif event.get("previous_message", {}).get("thread_ts") is not None: + # message_deleted + return event["previous_message"]["thread_ts"] return None @@ -260,6 +286,9 @@ def build_context(context: BoltContext, body: Dict[str, Any]) -> BoltContext: channel_id = extract_channel_id(body) if channel_id: context["channel_id"] = channel_id + thread_ts = extract_thread_ts(body) + if thread_ts: + context["thread_ts"] = thread_ts function_execution_id = extract_function_execution_id(body) if function_execution_id is not None: context["function_execution_id"] = function_execution_id diff --git a/slack_bolt/request/payload_utils.py b/slack_bolt/request/payload_utils.py index 9e9ace78f..c1016c65d 100644 --- a/slack_bolt/request/payload_utils.py +++ b/slack_bolt/request/payload_utils.py @@ -32,6 +32,73 @@ def is_workflow_step_execute(body: Dict[str, Any]) -> bool: return is_event(body) and body["event"]["type"] == "workflow_step_execute" and "workflow_step" in body["event"] +def is_assistant_event(body: Dict[str, Any]) -> bool: + return is_event(body) and ( + is_assistant_thread_started_event(body) + or is_assistant_thread_context_changed_event(body) + or is_user_message_event_in_assistant_thread(body) + or is_bot_message_event_in_assistant_thread(body) + ) + + +def is_assistant_thread_started_event(body: Dict[str, Any]) -> bool: + if is_event(body): + return body["event"]["type"] == "assistant_thread_started" + return False + + +def is_assistant_thread_context_changed_event(body: Dict[str, Any]) -> bool: + if is_event(body): + return body["event"]["type"] == "assistant_thread_context_changed" + return False + + +def is_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: + if is_event(body): + return body["event"]["type"] == "message" and body["event"].get("channel_type") == "im" + return False + + +def is_user_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: + if is_event(body): + return ( + is_message_event_in_assistant_thread(body) + and body["event"].get("subtype") in (None, "file_share") + and body["event"].get("thread_ts") is not None + and body["event"].get("bot_id") is None + ) + return False + + +def is_bot_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: + if is_event(body): + return ( + is_message_event_in_assistant_thread(body) + and body["event"].get("subtype") is None + and body["event"].get("thread_ts") is not None + and body["event"].get("bot_id") is not None + ) + return False + + +def is_other_message_sub_event_in_assistant_thread(body: Dict[str, Any]) -> bool: + # message_changed, message_deleted etc. + if is_event(body): + return ( + is_message_event_in_assistant_thread(body) + and not is_user_message_event_in_assistant_thread(body) + and ( + _is_other_message_sub_event(body["event"].get("message")) + or _is_other_message_sub_event(body["event"].get("previous_message")) + ) + ) + return False + + +def _is_other_message_sub_event(message: Optional[Dict[str, Any]]) -> bool: + return message is not None and (message.get("subtype") == "assistant_app_thread" or message.get("thread_ts") is not None) + + # ------------------- # Slash Commands # ------------------- diff --git a/slack_bolt/util/utils.py b/slack_bolt/util/utils.py index 738b6bf03..0abdcfcbd 100644 --- a/slack_bolt/util/utils.py +++ b/slack_bolt/util/utils.py @@ -94,3 +94,15 @@ def is_callable_coroutine(func: Optional[Any]) -> bool: return func is not None and ( inspect.iscoroutinefunction(func) or (hasattr(func, "__call__") and inspect.iscoroutinefunction(func.__call__)) ) + + +def is_used_without_argument(args) -> bool: + """Tests if a decorator invocation is without () or (args). + + Args: + args: arguments + + Returns: + True if it's an invocation without args + """ + return len(args) == 1 diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py new file mode 100644 index 000000000..ed3026d12 --- /dev/null +++ b/tests/scenario_tests/test_events_assistant.py @@ -0,0 +1,259 @@ +from time import sleep + +from slack_sdk.web import WebClient + +from slack_bolt import App, BoltRequest, Assistant, Say, SetSuggestedPrompts, SetStatus, BoltContext +from tests.mock_web_api_server import ( + setup_mock_web_api_server, + cleanup_mock_web_api_server, +) +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestEventsAssistant: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = WebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + def setup_method(self): + self.old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + restore_os_env(self.old_os_env) + + def test_assistant_threads(self): + app = App(client=self.web_client) + assistant = Assistant() + + state = {"called": False} + + def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @assistant.thread_started + def start_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts, context: BoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + say("Hi, how can I help you today?") + set_suggested_prompts(prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}]) + state["called"] = True + + @assistant.thread_context_changed + def handle_thread_context_changed(context: BoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + state["called"] = True + + @assistant.user_message + def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + try: + set_status("is typing...") + say("Here you are!") + state["called"] = True + except Exception as e: + say(f"Oops, something went wrong (error: {e}") + + app.assistant(assistant) + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + + request = BoltRequest(body=thread_context_changed_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + + request = BoltRequest(body=user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + + request = BoltRequest(body=message_changed_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + + request = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 404 + + request = BoltRequest(body=channel_message_changed_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 404 + + +def build_payload(event: dict) -> dict: + return { + "token": "verification_token", + "team_id": "T111", + "enterprise_id": "E111", + "api_app_id": "A111", + "event": event, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1599616881, + "authorizations": [ + { + "enterprise_id": "E111", + "team_id": "T111", + "user_id": "W111", + "is_bot": True, + "is_enterprise_install": False, + } + ], + } + + +thread_started_event_body = build_payload( + { + "type": "assistant_thread_started", + "assistant_thread": { + "user_id": "W222", + "context": {"channel_id": "C222", "team_id": "T111", "enterprise_id": "E111"}, + "channel_id": "D111", + "thread_ts": "1726133698.626339", + }, + "event_ts": "1726133698.665188", + } +) + +thread_context_changed_event_body = build_payload( + { + "type": "assistant_thread_context_changed", + "assistant_thread": { + "user_id": "W222", + "context": {"channel_id": "C333", "team_id": "T111", "enterprise_id": "E111"}, + "channel_id": "D111", + "thread_ts": "1726133698.626339", + }, + "event_ts": "1726133698.665188", + } +) + + +user_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + + +message_changed_event_body = build_payload( + { + "type": "message", + "subtype": "message_changed", + "message": { + "text": "New chat", + "subtype": "assistant_app_thread", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + "assistant_app_thread": {"title": "When Slack was released?", "title_blocks": [], "artifacts": []}, + "ts": "1726133698.626339", + }, + "previous_message": { + "text": "New chat", + "subtype": "assistant_app_thread", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + }, + "channel": "D111", + "hidden": True, + "ts": "1726133701.028300", + "event_ts": "1726133701.028300", + "channel_type": "im", + } +) + +channel_user_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "channel", + } +) + +channel_message_changed_event_body = build_payload( + { + "type": "message", + "subtype": "message_changed", + "message": { + "text": "New chat", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + "ts": "1726133698.626339", + }, + "previous_message": { + "text": "New chat", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + }, + "channel": "D111", + "hidden": True, + "ts": "1726133701.028300", + "event_ts": "1726133701.028300", + "channel_type": "channel", + } +) diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py new file mode 100644 index 000000000..f8ed97af9 --- /dev/null +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -0,0 +1,274 @@ +import asyncio + +import pytest +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.app.async_app import AsyncApp +from slack_bolt.context.async_context import AsyncBoltContext +from slack_bolt.context.say.async_say import AsyncSay +from slack_bolt.context.set_status.async_set_status import AsyncSetStatus +from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts +from slack_bolt.middleware.assistant.async_assistant import AsyncAssistant +from slack_bolt.request.async_request import AsyncBoltRequest +from tests.mock_web_api_server import ( + cleanup_mock_web_api_server_async, + setup_mock_web_api_server_async, +) +from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop + + +class TestAsyncEventsAssistant: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = AsyncWebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + @pytest.fixture + def event_loop(self): + old_os_env = remove_os_env_temporarily() + try: + setup_mock_web_api_server_async(self) + loop = get_event_loop() + yield loop + loop.close() + cleanup_mock_web_api_server_async(self) + finally: + restore_os_env(old_os_env) + + @pytest.mark.asyncio + async def test_assistant_events(self): + app = AsyncApp(client=self.web_client) + + assistant = AsyncAssistant() + + state = {"called": False} + + async def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + await asyncio.sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @assistant.thread_started + async def start_thread(say: AsyncSay, set_suggested_prompts: AsyncSetSuggestedPrompts, context: AsyncBoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + await say("Hi, how can I help you today?") + await set_suggested_prompts( + prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}] + ) + state["called"] = True + + @assistant.thread_context_changed + async def handle_user_message(context: AsyncBoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + state["called"] = True + + @assistant.user_message + async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context: AsyncBoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + try: + await set_status("is typing...") + await say("Here you are!") + state["called"] = True + except Exception as e: + await say(f"Oops, something went wrong (error: {e}") + + app.assistant(assistant) + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + + request = AsyncBoltRequest(body=thread_context_changed_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + + request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + + request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + + request = AsyncBoltRequest(body=channel_user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 404 + + request = AsyncBoltRequest(body=channel_message_changed_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 404 + + +def build_payload(event: dict) -> dict: + return { + "token": "verification_token", + "team_id": "T111", + "enterprise_id": "E111", + "api_app_id": "A111", + "event": event, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1599616881, + "authorizations": [ + { + "enterprise_id": "E111", + "team_id": "T111", + "user_id": "W111", + "is_bot": True, + "is_enterprise_install": False, + } + ], + } + + +thread_started_event_body = build_payload( + { + "type": "assistant_thread_started", + "assistant_thread": { + "user_id": "W222", + "context": {"channel_id": "C222", "team_id": "T111", "enterprise_id": "E111"}, + "channel_id": "D111", + "thread_ts": "1726133698.626339", + }, + "event_ts": "1726133698.665188", + } +) + +thread_context_changed_event_body = build_payload( + { + "type": "assistant_thread_context_changed", + "assistant_thread": { + "user_id": "W222", + "context": {"channel_id": "C333", "team_id": "T111", "enterprise_id": "E111"}, + "channel_id": "D111", + "thread_ts": "1726133698.626339", + }, + "event_ts": "1726133698.665188", + } +) + + +user_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + + +message_changed_event_body = build_payload( + { + "type": "message", + "subtype": "message_changed", + "message": { + "text": "New chat", + "subtype": "assistant_app_thread", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + "assistant_app_thread": {"title": "When Slack was released?", "title_blocks": [], "artifacts": []}, + "ts": "1726133698.626339", + }, + "previous_message": { + "text": "New chat", + "subtype": "assistant_app_thread", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + }, + "channel": "D111", + "hidden": True, + "ts": "1726133701.028300", + "event_ts": "1726133701.028300", + "channel_type": "im", + } +) + +channel_user_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "channel", + } +) + +channel_message_changed_event_body = build_payload( + { + "type": "message", + "subtype": "message_changed", + "message": { + "text": "New chat", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + "ts": "1726133698.626339", + }, + "previous_message": { + "text": "New chat", + "user": "U222", + "type": "message", + "edited": {}, + "thread_ts": "1726133698.626339", + "reply_count": 2, + "reply_users_count": 2, + "latest_reply": "1726133700.887259", + "reply_users": ["U222", "W111"], + "is_locked": False, + }, + "channel": "D111", + "hidden": True, + "ts": "1726133701.028300", + "event_ts": "1726133701.028300", + "channel_type": "channel", + } +) From 9b707aac8f228a4e68837b11d5c77c331dc490be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:38:47 +0900 Subject: [PATCH 027/282] chore(deps): bump cookie and express in /docs (#1183) Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together. Updates `cookie` from 0.6.0 to 0.7.1 - [Release notes](https://github.com/jshttp/cookie/releases) - [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1) Updates `express` from 4.21.0 to 4.21.1 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.0...4.21.1) --- updated-dependencies: - dependency-name: cookie dependency-type: indirect - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 2e5747440..c6f46a33f 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -4962,9 +4962,9 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -6127,16 +6127,16 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", From a57619ee1ed0ba9a820e14284a4bf4e54c7be060 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 17 Oct 2024 11:19:43 +0900 Subject: [PATCH 028/282] version 1.21.0 --- .../adapter/asgi/builtin/index.html | 15 + .../slack_bolt/adapter/asgi/index.html | 15 + .../chalice_lazy_listener_runner.html | 10 + .../aws_lambda/lambda_s3_oauth_flow.html | 40 +- .../aws_lambda/lazy_listener_runner.html | 10 + .../slack_bolt/adapter/django/handler.html | 10 + .../google_cloud_functions/handler.html | 10 + docs/static/api-docs/slack_bolt/app/app.html | 68 +- .../api-docs/slack_bolt/app/async_app.html | 69 +- .../static/api-docs/slack_bolt/app/index.html | 68 +- .../static/api-docs/slack_bolt/async_app.html | 949 ++++++++++++++- .../authorization/async_authorize.html | 4 +- .../authorization/async_authorize_args.html | 2 +- .../slack_bolt/authorization/authorize.html | 4 +- .../authorization/authorize_args.html | 2 +- .../assistant/assistant_utilities.html | 271 +++++ .../assistant/async_assistant_utilities.html | 271 +++++ .../slack_bolt/context/assistant/index.html | 82 ++ .../assistant/thread_context/index.html | 121 ++ .../thread_context_store/async_store.html | 105 ++ .../default_async_store.html | 156 +++ .../thread_context_store/default_store.html | 154 +++ .../thread_context_store/file/index.html | 130 ++ .../assistant/thread_context_store/index.html | 87 ++ .../assistant/thread_context_store/store.html | 106 ++ .../slack_bolt/context/async_context.html | 151 ++- .../slack_bolt/context/base_context.html | 46 +- .../api-docs/slack_bolt/context/context.html | 149 ++- .../async_get_thread_context.html | 149 +++ .../get_thread_context.html | 149 +++ .../context/get_thread_context/index.html | 166 +++ .../api-docs/slack_bolt/context/index.html | 179 ++- .../async_save_thread_context.html | 118 ++ .../context/save_thread_context/index.html | 135 +++ .../save_thread_context.html | 118 ++ .../slack_bolt/context/say/async_say.html | 26 +- .../slack_bolt/context/say/index.html | 26 +- .../api-docs/slack_bolt/context/say/say.html | 26 +- .../context/set_status/async_set_status.html | 118 ++ .../slack_bolt/context/set_status/index.html | 135 +++ .../context/set_status/set_status.html | 118 ++ .../async_set_suggested_prompts.html | 125 ++ .../context/set_suggested_prompts/index.html | 142 +++ .../set_suggested_prompts.html | 125 ++ .../context/set_title/async_set_title.html | 118 ++ .../slack_bolt/context/set_title/index.html | 135 +++ .../context/set_title/set_title.html | 118 ++ docs/static/api-docs/slack_bolt/index.html | 1052 ++++++++++++++++- .../slack_bolt/kwargs_injection/args.html | 53 +- .../kwargs_injection/async_args.html | 53 +- .../slack_bolt/kwargs_injection/index.html | 53 +- .../slack_bolt/listener/asyncio_runner.html | 11 +- .../slack_bolt/listener/thread_runner.html | 12 +- .../middleware/assistant/assistant.html | 416 +++++++ .../middleware/assistant/async_assistant.html | 447 +++++++ .../middleware/assistant/index.html | 433 +++++++ .../slack_bolt/middleware/async_builtins.html | 24 +- .../middleware/async_middleware.html | 1 + .../async_attaching_function_token.html | 2 +- .../attaching_function_token.html | 2 +- .../attaching_function_token/index.html | 2 +- .../async_multi_teams_authorization.html | 2 +- .../async_single_team_authorization.html | 4 +- .../middleware/authorization/index.html | 7 +- .../multi_teams_authorization.html | 2 +- .../single_team_authorization.html | 5 +- .../async_ignoring_self_events.html | 7 +- .../ignoring_self_events.html | 14 +- .../ignoring_self_events/index.html | 14 +- .../api-docs/slack_bolt/middleware/index.html | 29 +- .../slack_bolt/middleware/middleware.html | 1 + .../middleware/ssl_check/async_ssl_check.html | 15 + .../slack_bolt/request/internals.html | 7 + .../slack_bolt/request/payload_utils.html | 49 + .../api-docs/slack_bolt/util/utils.html | 14 + .../slack_bolt/workflows/step/async_step.html | 2 +- .../workflows/step/async_step_middleware.html | 9 +- .../slack_bolt/workflows/step/index.html | 9 +- .../slack_bolt/workflows/step/step.html | 2 +- .../workflows/step/step_middleware.html | 9 +- slack_bolt/version.py | 2 +- 81 files changed, 7860 insertions(+), 235 deletions(-) create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html create mode 100644 docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html create mode 100644 docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html create mode 100644 docs/static/api-docs/slack_bolt/context/get_thread_context/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html create mode 100644 docs/static/api-docs/slack_bolt/context/save_thread_context/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_status/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_status/set_status.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_title/index.html create mode 100644 docs/static/api-docs/slack_bolt/context/set_title/set_title.html create mode 100644 docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html create mode 100644 docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html create mode 100644 docs/static/api-docs/slack_bolt/middleware/assistant/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html index 51258e557..7a21b8bce 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html @@ -111,6 +111,17 @@

    Subclasses

    +

    Class variables

    +
    +
    var app : Union[App, AsyncApp]
    +
    +
    +
    +
    var path : str
    +
    +
    +
    +

    Inherited members

    • BaseSlackRequestHandler: @@ -139,6 +150,10 @@

      Inherited members

    • diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/index.html index 9e43e503e..9c05b7cb2 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/index.html @@ -142,6 +142,17 @@

      Subclasses

      +

      Class variables

      +
      +
      var app : Union[App, AsyncApp]
      +
      +
      +
      +
      var path : str
      +
      +
      +
      +

      Inherited members

      • BaseSlackRequestHandler: @@ -181,6 +192,10 @@

        Inherited members

      • diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html index eee38727f..d9f6d2ad4 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html @@ -78,6 +78,13 @@

        Ancestors

        +

        Class variables

        +
        +
        var logger : logging.Logger
        +
        +
        +
        +

        Inherited members

        • LazyListenerRunner: @@ -105,6 +112,9 @@

          Inherited members

        • diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html index 5529ccbed..cbf880d2b 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html @@ -119,6 +119,37 @@

          Ancestors

          +

          Class variables

          +
          +
          var client_id : str
          +
          +
          +
          +
          var failure_handler : Callable[[FailureArgs], BoltResponse]
          +
          +
          +
          +
          var install_path : str
          +
          +
          +
          +
          var redirect_uri : Optional[str]
          +
          +
          +
          +
          var redirect_uri_path : str
          +
          +
          +
          +
          var settingsOAuthSettings
          +
          +
          +
          +
          var success_handler : Callable[[SuccessArgs], BoltResponse]
          +
          +
          +
          +

          Instance variables

          prop client : slack_sdk.web.client.WebClient
          @@ -168,9 +199,16 @@

          Instance variables

          • LambdaS3OAuthFlow

            - diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html index 72237c202..c83e0c628 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html @@ -70,6 +70,13 @@

            Ancestors

            +

            Class variables

            +
            +
            var logger : logging.Logger
            +
            +
            +
            +

            Inherited members

            • LazyListenerRunner: @@ -97,6 +104,9 @@

              Inherited members

            • diff --git a/docs/static/api-docs/slack_bolt/adapter/django/handler.html b/docs/static/api-docs/slack_bolt/adapter/django/handler.html index 6509e1737..52f3c4909 100644 --- a/docs/static/api-docs/slack_bolt/adapter/django/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/django/handler.html @@ -154,6 +154,13 @@

              Ancestors

            • ThreadLazyListenerRunner
            • LazyListenerRunner
            +

            Class variables

            +
            +
            var logger : logging.Logger
            +
            +
            +
            +

            Inherited members

            • ThreadLazyListenerRunner: @@ -283,6 +290,9 @@

              DjangoThreadLazyListenerRunner

              +
            • SlackRequestHandler

              diff --git a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html index 5ff58c6c5..8e4df3885 100644 --- a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html @@ -56,6 +56,13 @@

              Ancestors

              +

              Class variables

              +
              +
              var logger : logging.Logger
              +
              +
              +
              +

              Inherited members

              • LazyListenerRunner: @@ -126,6 +133,9 @@

                Methods

                • NoopLazyListenerRunner

                  +
                • SlackRequestHandler

                  diff --git a/docs/static/api-docs/slack_bolt/app/app.html b/docs/static/api-docs/slack_bolt/app/app.html index f3a9808c9..b8d19c85e 100644 --- a/docs/static/api-docs/slack_bolt/app/app.html +++ b/docs/static/api-docs/slack_bolt/app/app.html @@ -37,7 +37,7 @@

                  Classes

                  class App -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None) +(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None, assistant_thread_context_store: Optional[AssistantThreadContextStore] = None)

                  Bolt App that provides functionalities to register middleware/listeners.

                  @@ -107,6 +107,10 @@

                  Args

                  False if you would like to disable the built-in middleware (Default: True). IgnoringSelfEvents is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
                  +
                  ignoring_self_assistant_message_events_enabled
                  +
                  False if you would like to disable the built-in middleware. +IgnoringSelfEvents for this app's bot user message events within an assistant thread +This is useful for avoiding code error causing an infinite loop; Default: True
                  url_verification_enabled
                  False if you would like to disable the built-in middleware (Default: True). UrlVerification is a built-in middleware that handles url_verification requests @@ -127,6 +131,9 @@

                  Args

                  listener_executor
                  Custom executor to run background tasks. If absent, the default ThreadPoolExecutor will be used.
                  +
                  assistant_thread_context_store
                  +
                  Custom AssistantThreadContext store (Default: the built-in implementation, +which uses a parent message's metadata to store the latest context)
                  @@ -159,6 +166,7 @@

                  Args

                  # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -169,6 +177,8 @@

                  Args

                  verification_token: Optional[str] = None, # Set this one only when you want to customize the executor listener_executor: Optional[Executor] = None, + # for AI Agents & Assistants + assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -224,6 +234,9 @@

                  Args

                  ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). + ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. + `IgnoringSelfEvents` for this app's bot user message events within an assistant thread + This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. @@ -237,6 +250,8 @@

                  Args

                  verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. + assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, + which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") @@ -383,6 +398,8 @@

                  Args

                  if listener_executor is None: listener_executor = ThreadPoolExecutor(max_workers=5) + self._assistant_thread_context_store = assistant_thread_context_store + self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( logger=self._framework_logger, @@ -405,6 +422,7 @@

                  Args

                  token_verification_enabled=token_verification_enabled, request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, @@ -416,6 +434,7 @@

                  Args

                  token_verification_enabled: bool = True, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -476,7 +495,12 @@

                  Args

                  raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: - self._middleware_list.append(IgnoringSelfEvents(base_logger=self._base_logger)) + self._middleware_list.append( + IgnoringSelfEvents( + base_logger=self._base_logger, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, + ) + ) if url_verification_enabled is True: self._middleware_list.append(UrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: @@ -701,6 +725,8 @@

                  Args

                  if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) + if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: + self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( @@ -714,6 +740,12 @@

                  Args

                  raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None + # ------------------------- + # AI Agents & Assistants + + def assistant(self, assistant: Assistant) -> Optional[Callable]: + return self.middleware(assistant) + # ------------------------- # Workflows: Steps from apps @@ -780,7 +812,7 @@

                  Args

                  elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") - self.use(WorkflowStepMiddleware(step, self.listener_runner)) + self.use(WorkflowStepMiddleware(step)) # ------------------------- # global error handler @@ -922,6 +954,7 @@

                  Args

                  callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, + auto_acknowledge: bool = True, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -956,7 +989,7 @@

                  Args

                  def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, True) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) return __call__ @@ -1395,6 +1428,24 @@

                  Args

                  ) req.context["client"] = client_per_request + # Most apps do not need this "listener_runner" instance. + # It is intended for apps that start lazy listeners from their custom global middleware. + req.context["listener_runner"] = self.listener_runner + + # For AI Agents & Assistants + if is_assistant_event(req.body): + assistant = AssistantUtilities( + payload=to_event(req.body), # type:ignore[arg-type] + context=req.context, + thread_context_store=self._assistant_thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + @staticmethod def _to_listener_functions( kwargs: dict, @@ -1574,6 +1625,12 @@

                  Args

                  Only when all the middleware call next() method, the listener function can be invoked.
          +
          +def assistant(self, assistant: Assistant) ‑> Optional[Callable] +
          +
          +
          +
          def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]
          @@ -1730,7 +1787,7 @@

          Args

    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 TZ;jJd-MH}(tfN+@Vjl88_V5Y+ literal 0 HcmV?d00001 diff --git a/docs/static/img/ai-chatbot/5.png b/docs/static/img/ai-chatbot/5.png new file mode 100644 index 0000000000000000000000000000000000000000..7beede4123cff6dab24a25a4c94b50bd47fa0bf0 GIT binary patch literal 131658 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̀S}1nR&HWW*(XV1Fj62PW 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 literal 0 HcmV?d00001 diff --git a/docs/static/img/ai-chatbot/6.png b/docs/static/img/ai-chatbot/6.png new file mode 100644 index 0000000000000000000000000000000000000000..e70c9714eb0ebbf0ef5063641e25cbd29036b873 GIT binary patch literal 47871 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%0D0`bp5YCX5;dKqb@Rr4d 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 literal 0 HcmV?d00001 diff --git a/docs/static/img/ai-chatbot/7.png b/docs/static/img/ai-chatbot/7.png new file mode 100644 index 0000000000000000000000000000000000000000..9d0b949767ee52337a40d9ff2f47ed66a244d0d2 GIT binary patch literal 366040 zcmeFZXH-L)L9(DIp+O{P1p!5J&Im}(IW|EF3IZxQgV5xhn$(SefRdrf z&@{QpO%6@^7W?e|p7Xuq+`YYj@3=4+y?U*xRjX!I&3fkZ%*97#MOhLeY9bsQ9FkWr zU#Q~X5K-dbTr(!T2ApZ+)M^1v6j@10DZi4EVpMi^Ft@Tb!@+s^F*cq+S#5(9Et>n+Z^;n`_S@!hl+5TVlQmz{e9>HuQ$g|KE^+u93~>eF z<294#>gv<@A!Q8?(>}Ys-HE$Mmp=dA$tor4GSvtQ4VZEPzGc~za z`#b>mZU6ywK+B?8m`-zZAkLS2CmzS149}tuBHP}U`1@ZNNq^z`xqyp<&rI9bw*a~C zOFf_^TK<3-XGr`jCApKJ}8zJ@LYmc@SHx_h5PW())ZrABRC4^sF{^+PtPyYJJ*>$D$Di=W~b7o6{y ziNfMpn|-eHwB=7VUhE2ev<>bB|1v6=07pe?pUBP`y(Z)rXWPIPB%`YrE z+mDQ=yX$R7@00X3az?%6dPc{PJ3->KI%yn=M>mL{!dlI>ztXc6#xfdLnkTR)7PBjS z{CpgJl~(8cGdV#D;t0)(259+|Lhx_9RUaiWsCkB4c4O(UzLoqW>N${OJchKfwTmoD{@+&yon?-#`JUB#H9R11GXt|19 zu$ss9D<7WW_6Jb+U&DUCs*OW4gQFinuyyU6kuc~zRoT_8XSZKU3HRZ4HCroRmHTd9 zMyDPCdm|x;7x_LqgDmAr{C5q{a6KVn zY39`?=6{E7kx#%U_&&R3B1qO7usbX1$*g}e1+R<&_J8*)-@1?UUcc1;rTz+g2 zgCF+MB@vwR(H@!Wm3k5|0WzHaxqruJ?j$OgPdp*-Kej(GaegA5@U;7W_q}fNZZUa@C1#3n zh-pN5q_%|Z4gPx#HyUsoX!N4&n%zxL%CZD$6-g{2UE1(xYil|5$?P9(Qf)GA@|Q3i zUgruOZJwL~ZZbIPi9*tT1MVG_GH-W^Dm^o zaSy~&Dax~yu*BV2`=Iqf=_6)wVPS6}WkGTw$Qj>x(fQFr$*=uRFbnIK?w7WMOkZQF zzPKg~NqWEVR$_^c_({K@?M>To>Y-c$#whDviy7weJhMtMqGWjKk+9(1lE3#+$tF zs@mT^mn!Et%FV{LJPCfr{jH2uzf(@`;UdQ(u9^Cm0?T5m?1Y@!=i7O6Zf(yWrhNVK z6?)q!U)ic@&Z3X)Uhw4pv+(V7S zszWt-d<9}^vW3a{p?T;$j7rlt@uKGl$AKBEyEZ99o<-MflB`0kIT2H(v@K-M5{&`MljoDByJ2-VG(KqKPOHFZ#jDqb&EExxHO zR<2ob05vp4+Me~ccZXQ0S-FhH*{+#3bk_VjC{C?Q{cazHl=3M!^F47pC_X^Z($YP+ z>2^~p@>--*WCJb36RYkwOV^h89({_=MX=?%D5j^o@fetq&>bvGl%CQuJ3i)HI*2QLRPf#^(Om{e-5P zOoY8d&IRx6?6V!L?rH8sZk25LZDygPm(|vI=4>zx-Ox7DdCD1R8wOW4ymfxaQRbBH zOzQ}W{~50Zj}6c8Y7)L2UNX9M_YCcjdf^&4i#PDDo$=O>>l4&_9Akol%t!IJY+pLQ zH2c}5yw<;%xnVq11~Hax6%X+VaUk-)ZG8KFgk^;K2mQM`cZ4Vw$u+2l?}!O_OYiki z?Nb@ct8sAhKRr-hUzWrKPf{LmRR9xi`P^i_tuDNjOU3AG{lckgFk}jnJNLv%#YuJ^ z?)<)Xcxu2?@wDWKj6NCq{xAvCjB01NT}vc~f8~0~b;$MiA4@*!-3k3V72WwYB-qwC zc+0NBXlHt%JSHQimGw@(O8&mpQB`eKCDTKduso1~h?oFO&z6((_TgJW3)?=bFIQjQ zG?PpW=Si-x{P||+KHryf#*H5Lu9EKam^+CsY+Bq}F_T{`;-KOVjHpK!Gf(%PPFIX9 z)^}-fVGNpeRjP06-fOCGi#;!B(Ky40dCF*fG8!P*9eIRtUU1>xj_JX&Z+t?J% z5~|?CZz>SZk3!AC7hmR^Sov9@MvnDpYPf629Zt%?D+g<4?q)t7w>(~Y?8Oko32g9= z)p?;u!?MGCV$)*x8+bj6QG7MYO(zHRQDSaB)dz9wXBA4d7~@z64XJ7sw{gGq^Yt?> z(iilW5yet7#%doxy+bWS9Zl_%WRg_hhfTM$bGH)T z4PWF6?ORDJ6fO1jL*UN^w-Dc<+~ta_`JflV_0VwD#$W=w;JmlkV0YgxRST~~zhTv7 z-&v&aY>sc#23;azB;oVkD{DKawI}4}NhD9x#D#YbBC~QfZoW8&Gq+@E(+f#y-++R< zcC7UdH>5NOU_iB_V2Qc&R)TQ)J<$aTTHhG#^2Ypn$Gj3znn@akM3yMKPswp3Rsexo z^`Z6dJ@nppZzS~D&W;*A#2$8|nhq%851Ke=`tRl4tGXvDu)s64Z#`RhJ~}r#-zx3@ z_`G~A;#V@l-}`i&`?lENh31a?95Uq48bj(|cG53QEs8xgo#@yupHs3~fuqQ1t&S5Qx+5xdJJ<(jY(z7MrxG)iBF?J45M`D= z`ao==z82?HD#zleY41#ln5A+e(0{@ow-bIXGNes10f4WbX4s%8VCQ6V;megT$=wmR>gh%XBj*koKP#AtACc!1wJo-J_CP1?LR;9GVuN? zK}4Bx<*(yw#+OCQC535$kL!*vbzE?8C>~z^;l5IRu!Vzzmtm!@?W(OHFKp^y$7%f5 z!NiQy)6Vg-7LKT=FmPyR=4#C7X=iKiBJ3%~^hXI{;P~=17Zc+jMO{m3odRUAtA0O zPr05x1py^ME?)Mo#-1R17v_K6KgRT56XuVzz>EYjg9j52+U{ZN_EX=19eH_vrNx%`t-U_d&aq+Ga-ePGfjAOuYR-a zANN}&BBUG{-b(w8d;i(~%a&kzyx)JKjI;npW3ijWzukp^0LM&=-|6e0W%3>|>T5@} z;{CI6{<Q0S~Zr#_q6xJUZ<%P!l(4c>vXsT8(0Wi$ZIvXzzfFi@w)}n z3A?^TT43V5`-a(jL@d*IJ=>%aVQU=m6#BmB{BCKAA;0Sh%t3)S`2rhiVry*zFRqM= zeWhX4Az+903E z$O5M6wOX#CRqT98-A2Jm0MDUytf>&;Z?7y%=xj?la|c9%&hu5FLZGNmR9e1&>G^(I z0=H4*t2<4p%B2o6pdaeLLiHV=tqPQ3J_!doUH_-K_*e7KSo%3zgUL8V!BM-qcGL^K zsSVl3*Sg~mzZ@^@DhLU#|iAD+pWP@%BC&MI>O} z!c@B^!!siK!m;{yB782=`Y!qnGJiSCe{_51+0Pn?DGi;6P`Y+(3$(t6B}f+s8kqaq zM6n9$(>m3`ge>a~-rwFZo@8B}(D>KzKXRaZn#DmBDdIObia)B5APAqSzgNw4qeYVW zAD`+kQ}9S;7z7hLz_v>687n(?m$LZA=8T?}8Yhc`y~EsivU)he6P=19U;S=(i`o0_ zTY^dT4_{IXtyhi=A7ch`6qA!=eECHcQpKwej<&_u3Uvf{bhU#%{=N;_K$mxoLY=xo zN@1_&6_WWu5mbDQ?&+QqcG#+k#l&rM^W^SNHtzDjYsZ9bybUJ(qiqr9!Lm{Dq;2Yr zbwcCY{J)PS@_=C_q=fH`^~)K$XhiIH&f6BOhSXF2<^xpW2D+1R`eI^@?E(ty(pr4D zDy@<|+I7f&Z{y84BXYGT%{^Z^$(m@j>Wy(Zbse+dS**R8WHhq@MJe`)pV*$q^1%#* zJzj}mDqNcPHE%KYyI-aESufw44P~GZ&BR|aVk~ZI#^R6asR#wq*w@}yNaD?(31Ba6 zbk;RbG$Sg3zI!Hxn~n)#iu?OT*{_{p5JcQWd!{kZGa7xp%u0{6<@0^1!R-z@qpfW3 zz2*D*+R7b7cy_O6uxq7C9w;}D=IDtx?@5A)iC5L#+wSG2e`$}T({t=MjS|tUEz5`` zKfdY<-_2kC`+58f9O%vs+R6SHy_Q}L(G7fchgi$G0Qr;VH^%_5$kjl1l{!c4-iktP zcBlPBojLU+J<|L&vff>ZPUM^`T*#xzYp}{n4UzpTdJxLzpCf+;uOQ&`s7jPo+q^ds z`MaCJMB?RasmZbGP)t?Dq`_22Ig!@qt_pP4=kcX(rPpa>)&{@_>wI{hQQ!-Mq&;Nz z-tdpo!96QPh2MoNfPoSpK5<(e;WEE?)2*1Ve1aQw(CN^xzV2~rQp_TKb~&LH}5NQJh?X< z#D!SE$gk*Y_fX?qHIMlott6UwFzL|5>vwwO9V)n-@qTbtme#Pel$x?V@?LVygmodW z@6ncb{9zWu*<++{SI<87x1bv@MZzNB&k5IgESiRjbL5O4>IIR{_hl!2o~*QTN;>y?#z`wgD| zJiVU1#y9`vXpB}g7}N)&<$zUam2Z@I;_(vZeV|CjODksYn#6Bsop>_-37A0&l%B_s zc*G&9uYOqdky2WUA$V~(r*-`OZ_KU?%?#6x2Au^3XbBSW{d~8|q-@K-3|a45D0J{W z(RuwMHQ3`Sp?bsj&*%{KlZO;Ac)U?sb!)7&Q_+Keo+dG@PhJNTXF1PIfzgU|BJOO_ zxjV^jn?pi92#2L08g_p^nFr4@KturdQ&;1uTkNhV71BL1+o}29o$(*7lu>|hp6F;C z-)n#f5Wk`tdXvr^OA_HYKLw11A`kb1Ac8CfumI1CH=Qx8E0B1@r2D4O_~*gIlU=FK zY5zFT&-5skr{a`KRM8`uQXNT8j1<~hl16I=nY5{JTa`L|lLq%z>=vtZ7e$#)d11YL z9-C8MIlx9i!7N{Ds}u6R`Vh@k>cIB~^QauX6>VHG^~dp(HeM?Vs*N z170gR$5-Ov{>G@lK4w$`)cCVy+)Hat|8f+k{Z2O4spi`wUq`bj$bqp3zuJx71mX`i z?)K_^Y>nY-mt_FQ80_YK$x_9L!w@aRTqzWKw&fL@U86?+sncLIa&O!s9a0L%vVp3$ zB&|NS#lJkm9b0C=Tbe~L|*LbA7Iksv`sz^Rojf_+!&p+t8`op=Mm5svL4FY zYhQDC+lL(N;z+$lO0Pe{GdUCHEm{YzGDcjz_nC;uRb>iHK z1^gKOx{I^Hc6Ac!0PkU+plo{W(6sM0qoRwGRX%x;iP8DYZ8TSw9{iv!rruaR4^+SW&8oP1L{4PAR?59w za*}U7Dq!H+nnQn?2=NK6`9UQ{bNVSGY2*ynNX)l;<|OCltjx4jr|q*zJ{7K5E**H(H2P~9HX)!=x2=U4S} zVeHoNYjB~tpUi^*a8ZG>V@^CN8~`it{g_wEj*>VR#PAQ?n@!iyGT`&!^q8>9wXK_J z@6jg<%tEL8h?9W9mK4jx?;YUBIWv|j!%i!ZxZVc8RJYMrvxpS;KC#2>!cw>}?6^Hy z=V#F-6|&y#^=nYI`h;^K2r=!0G1&1b*@TmzWIgwCXdL87MYo<|_Yg^r-hup{<;_;n z3jD8{&i708_@=0H?-)am^rt*7z~gAgmZR(Vy-y**6uNl_#~)D9aW#SmMqDR02Q=Fl z|BD8de*Sm3*B*ULKwf!4Gp85~T~pTEBb;TzFrrt=#50`fj*67|{O8#V9q33@?X zeME{kVra49y46fz(WjodVQzj=hfa(4JDnf4K0mEx%2ip}W^-1(_O}=#WA&==P>|Dv z?|7pD1qwR8RoOSfvyVYmWoT5Aj&#$>M$pBw_zUFg_{iq1uR<>di@;sTm3o`fu6NqO zHBp^!$>i8I$gFpJ1p4rlgWH9Gm4V=F*i^N28}T>nk?2had>=k^+@((p@#;~!x+6Gf zn8|l*x&cEz#exbW9HjF<-PUO<_FZ!UAGh4AbK9mKPMP*TDXus0Ldj|~Q@R3!8GdI2!~!wbs*aa`H{ihV=^9Vl`j+O5AWokh#%tJ{w z1hkIND44oH0=^OGi#;Air49xUUy3d3T5;44;8K!%PY_-TCA`%4mjTNO71z_XgPhuR6xePG_vls#(> zllWPXPeHvqHXo@_SX3cL@g}OX5TbkP-tO@>2-TUdOta*HbknL|n0qaE&2yJd1kcPO z-NpSE>X$S85i=wBVBEsuey8N@Q9b%rteTR@SyfdJ|K!ky{T_S_qjgmR1H()g;hAyH zpezGK>4-49^=2guw7FbKU1Uc5NNoMbHj ziqk$ur1~YL0UMNUp_FwmrmXcQs9bh?n2h5T;eWw-w{L_*U|QLiz~=9FfqwbG&bWT- zrB~f~NG8#K=;)_(iZTdVBBL)L?k2a?A<8j}BwSNJj8Q+|I#314%2r5jqY_c3-lg@t zd{aCakC*Jm@oxG>O3k$O8Vdoj>8bzpUSHL@_nHOy_A0%DOe2Gsx2t9>rb;4ZWZ{i* zO5cx3zyq5*YW4u6Q3@t|oeiyFE_NR;NnUbknnLG*7d3AcTfbNSo?J zR?uO$hhNDe)lDPa zXj6^SYGC-0ZxMGj_B-v!;==pJr6~=Evfmhgl5k%5S#@aWlY4f6fYsjOb z$}AC7lpmcMqEMrfZ5uqImDqoaXf0In;)6?cUZ-fUYxCCvd~&=wxpbDZP5HsEN9oyg z9#hX314>l%wqDg5XM;J)VDT!k>GxVVp`9#YO&y5s zn@n~45u5`J74}FWl{kGu%y)?@>Po(rs+1pUHDNW}dli88zF*?7Z3ea%%h*^gLsqY% zkukSC$-sV;7*=~=zufuf=N7fQ9azF1@??r~56rYxtcKP{ zg|+HDOUle>!U1WVoI1Ppv2vZ^)`rc-!aAv#DXyHlPv22ArIcys$<%&9e2%5A{@Tkp0ypICix#q_9gj^0St@ z1IO^3a8~Cm{A69P>Yc98yuRZ({>nnM5@)qlvNps0B0`N+r}lfScyB;HjOV@7DTqBtuGi3ESbZ>MDTo`7*3+IErPpx$-u zE0(KYpr_jLV!iq`v{ylJ(4T)}y3&Wb>`jyHC;0BjRT`1_zDbKnaRqThn@#0BrZ=0k zOi$E{#16Dq9F64%PFjeAuh4Qe?Kuw=7^plin%#>dvmUd+fZ24LmxJyoK4yQQ(#?#j zuRSL$;d|8h#i4PThSyR#2|y;R#gTI6#)Cs)%KO)TRo;pvD21=CirSd#YXxcWPr6o8 zgg(e(Gs?I2&Q0-^)8s2s)w-j33HHK6!>wd3-iEJe>8aX*P<6p>>IfPMKVSH;!fNus zC@MSJs->MQ$8W274q=UQITTy@aYwO8JGP5Z-@y6TLj}RE9e%OR+W91dfu|@k@BP(S zO?{^}-$L`3D5DGP=w;uok*arjP1Mepys%TBmNQ^^YUKWb&DD2PxP&%Hg_ zj_CnFzOrjxUJeb47y=Iuzo}|rcA6uz1p`(2A}9p--Xw4VzQg@zW@uILYuB598@H+F zKSSJc-M9)Q54KnRgy?oe(hVj_=z;7S`-I0OEM=*R^`Q1vC3Q=39tK-)yqA^+XVVo8 zgdqlgO$OAbS<)43*4)U|^~%09?7>EI$QYQ{{)!Jc-@Hac#(gtLn_+^{uZ4xhO83ZWPDkn8YhMNw85 z5E_|npt5A6susS!HqVUDlTUE)?)E#wLWsHQFIk;@_wGDNN{w?Od@E~#Qw%`8Vza3p znHu)#*qk~@?`JYK3N;=S)V`7-`N()`z?Y7;UVwMBXy-tz` z@1@7ro*&UY9#)jt*sB6u^zg~2rGt<8aKLA8O_?;0@{Q;-pUn_Ryd>r*dlu$6Vp9r> zQ(&j<(}EwMCC-HG;KxfkTY@NT&H+-F2Wclitfv^hYmBu2aX?KVm)X*}Do%T6|O)YsD&GXO>yUaIKe+Juk z5HNT?Y1>;(wy(D;DU>dCS|2x0E-t$D_LP3w3!Q3JSX^U0!0jT^$K63Yj!}}A)h{?Z zyEsofm@@6-MV;M zG@S+sz>f6iNf>opvVf6-b02&Q!!*6ZpINq=HUZORC8@CVmxyKw4<1GC`RowK-LGrm zqk^c`n_?Wq0Ye*ou%?yvbQGJ^peXVdq<`gdgTg1~c7x2&nv53{jhjDo%52PQ@ zM^0j1QW|<1^YdBWxcwZu%O%*P@(f+lu*s&=#w}?gOYilTLi|_oGbZ2NPWIC5tSDh& zVtjJp0l|2lK2aXx`MVQ2>K8=pMW6kAjvNp`krXCEibE#0@YD>4zI{pTJr;XF6*ngTV0!jICs>AYgB`~I4b2SK3G%fhWE7r-Y~saZInva`R zUIlK``)Pj4MM zn?zu$Y_^0|GLwMLRe3r?^XhWza*qGBx+YH3z>J$Plb~Pw%JG#tH~!bepekkI-6#w5 z`#%Yr7j@+^G$_4oSh`Ipt@BG=pN(pla0Ndk;7JA#L~PUP*jqNYkDCA_j(A9Eq{_QT zsll|1zs!8=-tf8mxL3T4nj9BwZ_p}A?C@u=0Ni6v;`WE{3;Ux6>0nX{;wb~Kwpb7m zrA&t7#o3|KT5*%1u6^BNMZnGnGUh?24*Ga{zoXf2_D;p1>y$EtLXtfIqP3>z>^nQZ zI4DDzKi`uih_b*05-_}MI$h+VvAf;mucA|_9NuIjP2O@D1E>Vg9nnX;G0Aj1XU$Fd z8ti$pkI}L`(p+({|E^qCrePoT{%^HKRGzhmtRYC#Y?TzG#FOHHfphk{2?P8|O)!9V-DOH@D2yIL@;!h}G6ivs-A<;W^M+ zA$00%aFaeZ{u`Rw6a(;!D-K$xAn6Qc2CjxCP6lUyThbtv!2Vk@NE zXwT;Jy*8NOUcZ!D(41U#S?$t<%^*v%?N@wa(-T4}I+t#4tA-6Id;Q7QY%5K0s7WYI zNz;8Wb|n}8FoF%@4?8XODx8&OTmtAoMTwK+KB7&A`&pjwN!@Vkh~O6CI+uu(y}sm& z#Nbt?j8q$9&e}J^t9e-!TY6M?ZDLDsbl0_G4JrF3$zi9pkp*Gf zFftT_8xSvvV@qHM6YD3|04*eMkqXunu6ltFovI}hmsXUW_Xo^**D5OwnH1Z z+t*I}0`UvtJ*ilN z`=lLof-S~r!-;-W-$h~L&>jh1gM3J;jVFCh+Tii^A(1R zBiUwC*@puF*;+np&toP;`NY<9OH%CtF$I52h5X-1xzB8fV-#nq5AN~pdcx4lsT2u5 zfh-y(x@KMEmy8&4`NK9TQf1@ud9yIvZc&rZ?5e8^mnh=~fIA}EKjc#}f4-XuB^)n; z?MBhqspT=Z2s(iP&4aV*m;6efz=k98j@LHibhZVczNO@}QE)Y+pFJ{FVl;sr7&f zD5w0a)yhAQlFhaj9K^{K;IYH{$RaZ}WG(C6TlH7$BTTo*n-e8LIau+P>}aCmk_`v@ z&&-lvk6W0zx0L)f{e^!`ydUA~4Yel=g*i z|E>+e`O^1yCH5z*wL=&YoSxFRT z1)b*4Y2dXOi~l~g?#$z&Z8M@J2H?Gf0b;!($F`(FX%ifzxvSCeX@9%!Q-*o@0iXfL z-Ka!QN=PnI>U%z)e&%P(5NmI0ePjYk-)4>yRptGjivPpkL%!U&(zFAkm8tm=4eK9M z*x(X)^H_UKU1##n-zn8kn6DNv>5tW^p;-vF%`r%3g*9G2v90e{BJ?q^!)cBuROC}3 zH|dg=mXjq!5jLAbh(k5FlFD|Tak!&KP?`Ilmx(mK7m7%m+jrzuY2-;Kn6yUl7eR&FKV+C> z^1t~Wu-6((q?O&harmC$obj+`z((Hd-ssdIMP^e1XNC5p+o%EYwS=$@Ef3^;RP4<= z8x?DuZXUl*#0xygiE0+yE#;~^r^F$KuknveE*{3~SIj&i=4y0ocB>;NVfU7Pd34EK z7x$jjub3wZrS|9U?A~Y_E$=49B#QYw4HIl&5=P6W^_M>X2nFYZ-Y}FXxbP zM11zsv)n%Bx~t!tY6LIvu5(iWN36yIjH%H8a@w!(c$hexMAXvJY4^xCikh!r>{gGV z*6LV~*YqgB?%r)~S1lgfTiy2fP@{goM*VVo975LoZIWZ)$ZboJU)2=)PNiwEq(+ZX zdIPnfR-$Sky5TD1xQknTlkcKy^cp9>N2gZsBj^SInd23VX~=IhxUQuDY_M34Qfi!@ z!HA9iBlW>Dlpap>qVP8?Mgp$RElXteNI1i>}tN$-6mi+e_p0#WJjy$ zwe#x{4T%xQ^4-jikmh1v(;+%uHr#3VH9NP49eDaFa-s38u2)W7-)AXt@P2x#pRTvu z%#;whH;8;j7tq^?u89LL!Ki(wNFL!yI|gKxrf<#P@{+U*+F~HVx|f2Jqe^+eFF-U} z+o11LDbKIYvPSifhM4=WZX{UT&q?7Nl8H#+ex5Gb!9FbU_UGrXfunh!v=qa`a>=UL zC^8~egSlb0d^IM4>$THB-bK;0ne6f!%p+Wjitb|n$aM|WR+!qJah#LTM^^+v8IXlV zvNg5o3aKShPBwjAGhsUhAUkC-h2Uysr{7IqxQAiA$LzK|2CeuWvLKYKuGDnp(|LWMz}j!+N8ieY?w)*7Tc~42DWwC zBz~~>bkDTU`TMFh474vtq=)iZ*c;g(S4{vk@cMNJ@fLq0J8Kpx%7rljZp+ zYWHEiNaguv0tWrkQXyiaPbc4m8ips)NRR1a=#-;z&A9n%mmWT?)=d_!4)ugK-F!AZ zx5EfW^ZbK>PzDWNeeG4xa&fk{>TJGKT0jfvkPb~2bTW}n%SvFX234!}$)&6{?#B9E zGik`95O`C9yAn;7{=;Nivv+%EbL^(@Yq4mO^t##B(D=rJ1PUmm7}BSok0};x%A**I z)6Mt!vU4#M55B&A7;-7jp6mh=mU(HL`}ujp+-$eW359`H`{_ox(*P0HWPW(D5`lSp&?twZhC#nT|;OyTuXU7e`2 ziSA}mNJx=^op@bhx@!ooY%WZ|;r8uHNwiLXx`$ag=HWoWhm^cga7pDB0`NJz;CV z+}g(lTc;V~G`@6uG%kmCT9W9=cT7=ltZf@XkDfpz#bXJjUf}l&U-gP0C6c9!_Vs%n+SS@;tv{?g1BF6d@mZ>uii^69rEI(&HclD zWhR*YZNh=BT860&y{!zL&FPP&v-js`{$Sp=p|v^Cu<7*v!0}A1 zQuYa^NdnN3nKAq#>t#~dVcaIUL!$%B>3xWBhd-jxhMmveWmJ~R5eU3kYfoNt!POf$ z@hFeqJyi*ISTT!JRQcjiJ*-)Xgl{I-BOZtYOp=?{un)tbB>>R&u_nf+LE8~n^B1z2 zC(|*e;BB+*)zDbNW#8krc^41(++iOx#f}CUO9Txi7KlV02RM(z|{EY zu7t8fq;3j5INR81f+S2BmX&!87VkH9jPECaE>MUhkEK{7I{*{~3*Hz@k#+s8r>LrV zrV&J(E1&wc-A_aK2IAJ?do6LrD1&WV@7Z9=!B>w`)f?)8%u&{yR9azI%lhpYk->y0 z;;6Hs2&qA>R+b#-v^V<(AW2o5zHr>UWPR%oCTtK!r1dNcr#s0LZj11bHeoh4HggFzB-ow zwRPApj+jIjFlwfajkUjz*ODCAL3(k5a9-oJ&(UlsqMgPr<)-jf#@=#PRF0f4msYsK z>yoVMw8OkwQP1+zi~Fx$_cnb!S;7n1_6FF_oTyO$lsVYnCpiWtvMQ4!mBSaFSsk=y$>Khf;Vbh35}Hi<_YLA=Ke?Ff9ziCn&u zQ9XayI(8?Rok{ujF!3nr&Dq}fGtvdIV(oaFwGybk+@r;~^FwNKQs$|;)19u|`%*-q z#DFxo9rKf0E*%a&7tl`gkiZT^4JO$XCt4_Y8IXnOx+j_;*b0kOr*BHvtd5o%eW_-R zpcSfE#yV@t1Td$qdBTaS2eM=-b%*o(_s|gN#whg2jgPvD#C>mL61uGOhX}cjmSC?} z$dBFa=26|*G6eO-gaNvyZb9Q4glo{Cj7KBet|lleif6CFAQtoTQ`b&#*VxQ$LQ zjU0l!j{l~{>;Of&TV)1QS|99ttYOo`x{wVX4hK}l)uut$@rm?aPdfdTGW%bb#$(wG z>y}`raw}x`)*)E zOy3hA3;}k#wv1!#2po>8^t#p?J?F6#j5hj-7iYF1O6+jA2}sAao#kPbu~~2!A%a-~hLAE-Q1-OKMd-wztM^PGXYiIEv^B zBbr$G`F7T%jQVmSK7}cf=&57F-xwh^Ev*+i7<@!wfGp-FP<$wk#~+O6(FJPKhuP6^ zie9wrN*5mSP5H@iay4$XNQXudeK~H4m-gtabOxNZm-of!{;%9)sg#S!Ie(1!S(U4F zpYwZ*3!3T;82di0Lc){#ux-FaV1DR%%0SX$sm>8fauQd{kpSW}diqJ&(sA2@r#(?a zHwYo85yYX{Q(c_`IG8rC8$SNpHmSWggcMKD^?e80q&w1lHtP_EKA2d0ELveD)dTg4 zRAR^R4;KmSjwzR^Ti;G}j~N10QI&lpUN_1A#%`CFx*r4<+@M_gRn_xgdR1&)hQ>jC zYwr9E@56z?)~hk0`G4lf^n!-Gz>Be3gD!pKH_7iN-4+Vb21Mf3PeXwB*uFCe ztUHuvoBXnlP0xjfGl%PuO$j{bGH4yuZ8&n0xxw(o(C>J$qP<}^QY74-vN;Q%LOWxb zf5UhY;HZ-2G=_ZuxqhM*!n;CiISc_a8zQd_RA|50YBg|Ncawgv#4?unKF%I3GVNo=RjCj3z$wcfJmC2Iv}6r?!- z&(2+3zgnQ7)m+sNWFRyRVfKN{$817poBz6O{~g2jWAQZZai##7w_&fu&u67p>d2H9 z=^Un@rh^)#`f-L>*O$}#!&5>4Rg-HLr^AAI7)i~?CWif)whGGG`0`@|4V( z%JzCYjlJy<1Kl5aTQ0>dxG8y5$3WWp)DzI|8Wt)+e1pnj7tQM+!iO^evp!uq1WO!^b;WG>l^NNy$B_Cx}ZV9 zrseq^s2YDDI;^;57nq7~HDYLuMF4U;E_hs|- z&~wdYVJY%7wAtE=-#^}Y#4kJ#Xpc`8lh1kIxn1T!(bzH20gkJ0lEFAgRin~<5)5rT zSrTSu^k~_B^cGRGgx!I)3#EWZ##cHD&jdjY-4!d@ca*H1A9JH>>)^GK>9*U^Qzp}+ zf}yScKAuUUUe1?*@G>s}>5c?}>VAB+!oUs`(?1<$PuH*)l}R=~v$SuWv~K`iavODR zGNpjd2x4E@2C_aH2jCmkSwk+oR)g8DKbu3d%T3!UU_S_}k)HDrRtN=tg>bGK&uJfZ z*MO&5GFvs;J}YhUVZ|i|&|OZEQFz}WON)psgPUvTp-!Kje=CVVc5b@2Ff`$iFGm1_ zYVx0U>seXn_KEt|06ShvX5G}$v)8k)Hd_VutpPRB*`PavG^O!rUq#-105bJlbiMN- z2=iq7IFmY6yGwpnXlOu|Ar&k@KJjF;t-OOiInK!bxC&U55Y<4qxi~X#J6}_)I$NJtJ;8_$zmvjU&2kXz5aDCUAy7o z!C|dx%xdC8uCaf3ttp}TkJ+$!@{6zh`wA#$?Xz&UK^#iN4n9_6)od7^Eg;U2M1Qt* zzBkknmHkWY5o8gq!+Pc&%ZzyHQrtghTHS%mKmB%|CG`NiCQzcAK=TH`hg~qxn%gCG ztV066G!f8(^;{bOF_GV{YF1_iezN&`zCEH4$&bf%nRuA$+2WtmAPOw($^iL2Bd0d< z6_1Y768A~DMj>ES)jA^EBq9L>uN2mAC@(_ST%v!nl1@DdO(VflYwNE~k-4(}$ zx=S@yN~PS=j`?z*fMC*^pZqXZOFvQU5#mBbz(>6|tn9VAfWA#}bwz+>(B#~Co7iQ< z7Z$RcG+zZsR}{#NU`oTEn;3v>SCc>S{&_z%Tks;JIOT+Dm>7z(KiO;9;ba5mL3GuC;{Ze7gZSh8v55Gln|GyApSQLFu$wN z^giaJhOS#rwd^l})j2*9!MaJp;WU`b4mLc(nig9>yL@E(<8YIO#^GxTwnJka&DiqQ z$q&Mo73bv*n&6N`wdGv900?v9G>f3y%sCO#Jq2I$^YweoUjJ(*|d&r+nnS_JCgzNJ7I(}m|&&e`&TjP0YNV!5)q zdPevf%Ct{xhh%&KP8ej@)OxUzLldrL9(GGB$Z2lkQovsq&5|^RyF}cYGY+MT zX1*oM=COPDyU>X5?(wW-hB9=f73v!`whzq$8U2ywAI*^B19K(~(I*yI{AsS-OToAhu`ZNm#*yC@V5bUCT`85V^~| z$??o+qw}43Ry_H|nmj{6ApO447#3oh;=P(L{a%6u%2n+Pke|7#8fa(J7dYrPN<(?( zkf_JrB`S%Vks_NvH{xK1&MOaf9%mU^1O@{ufk|4U${ElIUj&U@qC~u`YSgA#ydHns z87EX4(oriww!l6Nq%8YGfH!zxL*{0sMt}y}In589{rQC%k!X>Us8<9wa4F_f6C!Uj z2_>wTj;&XHo<220HYL2Vhj0!j_lZDrN!lt{Ic>_)JH6F8EM%Rpc{@p%cM1KZ3(>=L z67RfKA}X}Yocfohay-39t`YZf9I)&hNe~&|+6cVc2)jIBYM4w7Ivx~MZ$}7?4mSd?4 z{||fb85LEwZh^KmD1sm&6pAEKKvDrpk}L|6K|+zUNX|*Jh=oWH0SS^-a*jo&$OtGP zIp>^nE_kc@+&kJk?zwh(e!L&=IR-y^*s%9r;agvtbFS1hGu15881Aw)&snN_B^}fq zM~43Cmdt~+l_U@R(ni$MA-%X~c7wXDgmMw~BihcxTrqsbYY5cwzP#7*@VJ!Rb#3Pa zyD@Z5s=nK3hp-GDK5?H*uPETsYzKwCyD!D-BBK4k@FGYqON$>up#DUzgD@Cp0$ z#FVuwR`-TmbEXFL_FV(ecn-eRqV9r4v*qkGbw5xH9JhDg8?w<>imcxU{cP#S6t<6) zoGqP)shv-3)g7B6&cB_iyR~2kYE8*u^7qR=eTusHJMf*0Gk1 z+#TJ{b?b_ETOEwODYaIJ=0<^DeWK((mk`-&3Rj6*W2mU&6?e-u#}2N3Eyk47w|bgL zPn49-Luqb5(*Z-NNyYA=K0)#EYL9NTW1A8H2mmS1E2Tq5{o2vml59<-Q?1NhIAl8S z%0bq*DIK`peC;f1oH7hu1)>iY1{4f+$Fj7sp@p6a;i((;hvk{tG zI*Q88{)={Phs`M5_a|#n<3_!EXfNelnF3?A;FWkK$USP&?XZqqlXV_*G3nq!D|i8L90wxI!hyYYPgy&{E4} zm_`09WYA$IHP%jQuhI)N9D{t{9wPy=F~wtlm)X`x*8+fQq2rX){u4Th^W7vD%Zagl^m^%6L^A zvpLD5v*^`w#2$UhJz+Ex#QOqOR1V1TcLofDlX=FU-$T>E*VD!C2n%JR$;(4d6_!e` zN{k)wJN_i(rP&T#&pwDp?IX{$H}W`8mdZ#}%bc*}=IF@#KXhv!FjvyHGcq{?=7RD| zkF7ThO3H^815@{^`!vjxqx`F_qC5fm?^_DuZAR33^|lo{C#=_IoxkFDW^>as#y@%Lj|rmK{;kACwbZ>CT$|PwL?!mcL@2iayd4VvRAr6(R1Q^<%N=2~k(eT8- zsDdEFOnEad^EJb&v8NebK}F*)hh-+{+PA~xg(N}92auj}(p9eeQdMrdash1l&;2K! zBx}6Vol-Mw3X}v~#kQBeXr^Muw?<{eEVdZ4BXrd?7MG0h?D=zYek%)z+A4=;u*K3~ zHR|XY>CUrfT!@3{U}J{;EVj~`m!^|qkoGkr=|8nOthC$&n04JZ#UM0+=2V?tA{quQ zbkW+Txn2970OdZo>jgM7gY!lqa571JXpvsbedL^$H8wq9$DxKg%;uqSO76~LY5XZ_ z!SA-dS|xZMnI7p4To~N%Yk0#oo?A8dwVh2gjswTa&3qGe04Qk2-Ns%*N84XZ&t9A| zX28XF#VwmIK3YJbG4t{J2LkeOce&igrauI)7XmP!R}8!}=JcN;-+uVGny+MN=YvC*Ak12RBFftD|6+3C%HlEO zc!_%!H@j%mtDv8EG=BIpY$nD|C@6~8qIFJ;aw_Qcc(=CYyLrN@)4x8yXZzm&!`B9L z8S=7vCrJ!DkM4i&)i_2SzIy$-9* zBQ6c6xWxpava3D!5+44D3-aT3`q?_nkfPOQMtZe?&PrEU31JH>Y<4+%I47Er*FHC+ zlABEIhdKS}LoMYKM_=Rn?EII&PNqtVq}4agYuz-~$1$v3U~z|{Upum3I-^mIH~frZ_gU7Fj=(t#zxB~f* z#qRyb$!w3k!s%sWV^ic4;)j!DEC1RWAXr%vRL$jFPju;;ASIJ!&MDN=u-QBuZaBPu z5##UY!pXR+#cMt$r7DZ@cQW1T%)eWI4gTMT@y~yl201rbXI2)LgNf$BzaFP7AyS$r z`p);KOw|AJ=3ZK$gvRRJ<@ooR@>6>T`VV+O+YE-=#r|_w@xxORHLzddXU@u%6Jk2U zGzvGt*zq2aW2`KNs~i9{!{EK`)GrRxs zI1xA)J2SQ^rW!azGzzRh+O5})LHoi_P4dTqrM&}Vhtm*X>>sf+#K1<;SW8;|VVVA6 z-bjhT*ol(#F&}y{F4!n-aSlJHZ2UM~iK1Zaq%S!yVLtTVM)KQ8e%wO;=Pv$jB)>fg zMrYS=Px9N7{ODc(Zx{JH-1;4EVYE|zhg-jM63lX+-#N*z3PoQ8xuWc_OsXMWuxEw0$sWk**KV6ENBSHpb1K=1w7ceMFifLg$~$dS}Oqzp*zaX}gHql{Br497?TC9k;b` zG!oU-xO`5-JO#jP5IVZ{WYws>v%!jW-m;7%ubKo|YY}p6EUa_Ms>$b3ipl;CNsN9H zszdTPSMq6OuMj(+0iCu5V1(EWju;HWRgRGK93E zb~<>c1EaXiPhr=O0mgjlB|g-ozh%b;3tNNX9qfAL_S*__OD$DZvRmRsvJ<7>FDOgS z8Yg%{#z(Es4#u1>fAKu^ShiHzn$rpU=a=T$fQ)r+&i1Rh;S>-z{E&OSYNCUzVWuT) zKJzlqRhm3KY%2GSj}qP!DN=Y;edBYb>4>EpWX}OUbgKTcu*JzI4GgT@n-HxtdPow7 zYA!_c5w%w;gh0aEa9Nuz=NA0YKzcy33=W z-S|m0SBK6KAr764S`Gtzm2bO(R{wYG^LNg*zzdRgF?(BpDCewDF?-Tor>_yW3=|hx zWBCEL(GY(`XlpMmGn4uHOsA}G6Og-ARoox=9NdToOexMM?P8f$xL{HjOSHqu;M3v- zP?XRQF`(xZyPvu(wWPs!i28sGRxf-;8jZ%>ppk9736VY@?in|2cL-9WCF*3NIZbuS z?~H^YoIOtRo2LO@>aD!cVUmI{&joZF1r2oDOgKAXK*f5*)&EK7 zWuR@>$Pgy{rbtx1`qB+<7Q&NS?|jglp3jUvk$F%Cpqkk;NA3LU6!OB`J6gFfcmYGU zbc2no>0XD>)!fPxDUu88=PywZp(PzoRt^#xL1$HCtFlRsUM}{UalFg3k$X$=5|edP zp=0I%#}QCZ$j{Q3S)!XC92-8|x3?LpU|gOK6mRl- zc}n~(5rg#QKsPEe;p`ZB763$IIMd`mHULnzFur~Yk&&$gT2(3^;G3FnzNR7u_$zH6 zw`J9$a7~nFTuD%kVOiqp@p_eR*WjF{1%!)>E5FwFMgstI5;H!HWh)7;yam8^<14P! zR#w7Fi8ceIs!dArLf5C8s&o!CrH}!cIS4a~lX@S6ve`&;l@i7x=X!$r!u6~kRa-q? zlpt&2*SnjuY`NC}BG8#-JJ~3Dbvy52@E167b3{?ubbAQhd?N!I;YeY75bU365Mx>L z20*juGu`$_ou|j#*4j?no9C)KmR?hKm#$YX=Y$@kd(XT8pewx;`?m%D-&X2y$vcCP zEQMyEL;k>J1+a_YgHarY;wO`rz5GBAxk918r`9wz4rT|0fO2d{&iJ8DbeY3ZqP>6sLN7?~mPb!mPfR zt}B+t6L#UfW;olLLHiF$k4;HDYQYcqTB`-yP}`Z1$4wl~RNYVgn5#N&UZ5sM15a(q z=$cK}td93B2jyGf9rA0X+i%s3!d0Z8g23?(R-~xl-CzbS#2|+F=d^Bnfb(?&DL7lg z0oZ0u(a-3&m-|ebnLr;HF-(3cH{I{ zT8d6R+3Q)c&hhLfG4R+4c|2WaoDgu;^R#fljb@ARx={Rtgd}8Y z(bBV{va{2(24`f6E|8jB3xO$V6U_p$g1@xgxUg(Ei9-&x(^~XBRZNN!d?__ACF2~v zItLf|>u6N5Wjm>ASy6ZzBD!>JwNcYWVjF*+c_b#ej(h9}v3V>?6TnuOOqCr%>qIV4 zJ=a6?J!Q}12SgjA;=RTW)vx803VFGM-!;0~nDP>S=(Kpx(%7{(T(!}$Uq@9d4hQeD z@X7|g51cN@o`F&C`O>-^Yx?QD@Y!`ui)yjL+PbM5q4oFYW3RSUrEe7`oMsK`=_~D% z_V?=+Z*?v_-ki?F(d6WME<&ECN^&L9O46@b-hs-IO8R zGD+Z-sS%AoRNp2uy`5$j-c086liiNB2X4d1_&vZ)siyTgl~GD9gjD+Fho|{ zYnGByO(n8m0RoG$Pj=Drg#duZ4Y!cqDXKHB7I=?;JB4%~tsI?e);pgh8HTsowTA@i zKXN(@UAT z(4JKGVW1L2%-9pv+QwEzVR}3>wY98!#jiS9EP)e{o7l&Yx8^*UTUrZT<)g_i2uFA7qbWVOZvvK3Kv3HyMG>MkXBbdmN35cAeijFbb=!?tVOEB>M$cxz)W z&D#0%&U<%yiuV%b6UH`|$4{8Hkj#h2T}<%QI9Jz}jF*5x5`dN>^R>FmInHt{urEJv zq(3p$%4)9qFznI0`*O@PqJFb1g04G$)D#YSdwx$o zubC1-Pn!CXLh`<|6ou88bmgNVX-k$N0qSoB*9Oi%i0hUU%r2F5ihU_=I$J&!3ghIT z>3ry)sUaYzc~L%&uecM6v)+DxKQ>fP|J0y-FCfhGNr9aP9s|UB^XO|~$4m^FkgBFf zah>gEZKyQ#ItYQC5q!F~D8T5xlHk52X{*B7`xtLJnf0YoVy@?6SF-daY9WEs;~KAN z?+es^AaW|ctrj(u%FS>()o@fY2Xk~QdoT*%>rWK-v#mWc!T&@KAid58Ds|MdPM7@B zSB1|uJI+sbUiI`8mCR!wH$IY?md_NPlh$C|jEF(viXp-^tGyB|**12%<(nJcScf~r z!iH|J>xoEe^9?S=XmYCwvS;tzFQM~bi`{K5B*(^A5(Z!UWTl|3ouIsOF@?=Dx2usP z%L9m1N)KgMZb@wxPJ0ba95ZnZv6pO3f>>LlO9)jRpD>K2g;8Ugod+Zo3FgV9dxX3b zFNiCO9Ib|4mX>O5cbFT1)V5K@b|(0(MP9hIq>s2Gyuq}z-BpG~YEBE7`>=Z0y?7Ab6Ju51Q- zo!p|AkJm~=p;IB?dH6I<*(|wS0YQx{C)uag=Rz}}#_1~dcS#OKl%C>w`$SwLaUOM1 zEi6eP{o>!&mg}D1I&PN9rIl<0$Z?x>Fg6gk?2lT6*V09(eLC1X-)x#(T$G_tu3H^Q z$hZ*5Kmy|SSEX?W=uPsX7FB_5nMJ&} z%bt#-C&_ijw(RUGDW}v2){9P^@TLUnB-WUKHh@r6c$oGY%XKwl_|uwtANd>cUkz=<9W|Bq{{_|vhDupXW()Woj_sqnfqZIGh~SZlQ@CML=H5| zB^clhgh2R!7hnkSivcq5&2lbM2xKU^*SIaz&|3d+^Tovc)%VimoOX)BBN5R_q{Pd) zp#xvC=~NmsPgs#BTls)W)(E72G`r|62i;n&<=+$$Y^x&odIv1~M-5WroXzZ#8;e}0 z7gG~-x*p?cqFK#@9YEIn+W6^SiDAe3%5~G?<}yZ8=|#?X$uRnFhb{1oE?e7l>O`4- z#JK)(sLdo;d~$wIx8?!BHVLr)HaDODn3_3V(+jmL-JU8N1KCx5Ep6{U;e8>Z$b9j3 z_FNRA7y#g%`-)<%Sc4jO=Wx~lx;YK`(g<$>eQ7|;jkEQ$>MLGCXD?Y6BCR~g_^hdvZ<_9)3WcFPNEcLA-NwX3?QB~|Gq#XNTPuhxrz$ePr< zzLlIkx`jH5_42-Qi`?qX$z06z^iFrX31S=U^7pUsyAgG!Ts3h{M^1(N-@Y{hBq_Ja zDIj`0o2)C+c&5QxTzcsmd)`4&%k*g(>6PWkCfI|l@uvG2RqQH32%|H#-9d7Ve=f>! z3N5{>b)Cmb3!N1_@iHGtI9*BoUS>ZpTG2 zu)CiPb83!rZa`m5WVs#-TQtv-`EZv`4hX}t1UdtW#rK?&`t{%@3YMJGDSzJmSbJ#% z3)CtK%he0Hw7v<%M8q^C*^w`Gt7Y*>UcdH|^*iO8I1(9*-rQ>@W~4%_GoN zG;|2VH=J|Wbi+FEN=c9(T8w~v*y{Pg#$-Ow!g`i@7b2XRl0x5Q<_*pb>k->yO4~N1 z)tlqBDz@SCrKQ?@RNF&Q2j6nNKxBT{$Yg`AVIc}87!uqSkuI001bBV3*l(XFASaQA zmOZ`QUqoObZS-A?EJ!$RKlNY^HXcaw)*^!i==R%!%(JPzVnvJiw5>$XF`4ka z>@AoV^Md3{sZh_?5n6T%uy9d; zf!=9HRm-z-e&Ae0)!Eu#2wU2X8G=S04=Z32O5+y6z%hCy9jaaaboe|qxK$%aD^AJr z^w5d5aD6e6=>3hSbaPw=$jCXV?&N3rSP~YcYJ)LuwH0aI0fB$ z%S&5bJY8at94_@x9etHZW*wRzX0TG#KL+>Q2Pe7OoOD|QNK6d!#mL&Q0!(r0dR27= zyGmo2b=4P{={np`%jOF=W-GHq2M6ru8ng2~d%h)z481%C^w|WY9C;Z(4rCZ08)0WB zK*>=mKCgi|AoAoCS}|g`2{@-DL+&S=3?T^PwXrJAk2(21`(xJEL}ffkBT1fS>2^g- z2le(_&!g}6`KS7>-Xxic5WIK;=@n*OT3_PiSInlxKg?I^`B|1{l#_ex`Sek5wvhZy z1Q1K(P}GC!QUP0MiQ>_lh-H1Uy=%Go0U<2}w;n1;`-&%cymRE}yo?GHH9=<+gi0QK%BP|Af2b*w(Hw6sJJ8wmM@ z$J@`Xcf<}`;jMPJhI~G|S8fykGK_Ji6cDk=mWGxRi>)ko1yJrcZ93B2KA-kYFp@lP z`jG1NJq`Yu!?3Bzw)qqp3ImlLYYNwao1h639C}nH=(17!nige67FkqaY%7^b0C|ny zjTTI426V+IBUv-N@XOS0JNcpIMU7i`+gP*}c|b@If?#8VE?To7S?z7B4F7R50i<#1 z>VmRI?#L035a5WDbIN79g6lFWb2XyYZNMElE!58v(3Pa|_^sj4@+P^iy(ZJ*Y8OWI zy(kt1E%bCHDdlC1z=;M^B8v@MSQ(%80v2i38mJL5mqj3it)ObWnNBdt<0d&OmxB;M z1?ItvRD8F28F}`w5=0Oj_H$oFJ!>@yAmCexAE~Mu2Ta{1d_WIHP!N8hd&wWDd|1SST3C<}&!_+p zb~vy3AovCkbXe3C+*CslAO2G7^BNT?x?t?SnyDM`+0$Lw$EN?Joa=d-{~_`7)nX2U zIbloHDiG0qb3_7h2_ql3F32Qnc#-Rb7#WId*sh|~5)U^a@)(sx-9RbuJY8b4dFrmi zLQ{V2`DHTIM)R_F<3N%(dYH<^Rnmj+umg4W2B~GEFX`dB4)n+?lb~@)mW|7i`t>;` zd5*4l8>fdS_*1ck9ogfRE!*BfSv5yr>VqMtR?oghU0Fx=+bqw~^E+VDr_}StAApqM zY=QbOKiS$KP18)B6yu{3Ajw*6^LQDW8onYYxY{$|oZumCRl3sER-mWrI;RRdl0syg zYZR}a9azuHoK%{b39_Vzf#xprb>nh<5d42P zd1nL2WdTTSCl{G`fwIo^8@b4k;g=_!BZ&1IV#rgJ9k?0e+3?-eaFzKKpwwMIVSso2 zY@)`8*5mQ*uxRpOM-Q|B zJB^MKUC$)Ia;GD(V_;k<`9d2&lHq0B#fJCsQ&HC#AWs)2i28^V5_V)i+biC!hLzfZ zI{=>6*13ym1g)1E2Dc?}AAJyghh|1j4)qON#A$}FR;FcVv-Tu+>KvJ7B@vB-aAag4 zSyWrYWi_GyE37sdPfE$7yN$?~OVek}d;)NL)OnS22F!(;=DNZTLr*aS> z6eQIBu|(;xI>dFJO|Vp97-9u)r}$bTqVe6jXLO%)A@wxl1>k)?$p?ALW4zKn-75|a zmba$!>MvVP<$S42H!bcY8ITtK7A|X-nPfHi1@FybJPh;OTpk$aKYsLLKgL}1&1R$cUu?TOPDxZ7X zo$+Qq&P%7o%-l4*e)_Gs(7ULkmA-7v*qyepsiEZ@)-q)Nd~)6ZP;-ri$9L_X>>*55 z>d^Pmfe7?HP8oRxPj7PX6q-WMRay!9S504rA2+#{k*;cJcES2oQK*bi%kbg4?Bb68 z93$_dR%2B79z@&WJ4m7KXSyRs9@ay~N6H;4rh2B_G zI`Qp>fGse>;EJ_s=3xZTz@u)5ZDmrwmC8)DEcz}!$lYjKTytau?!>S?^I8iAf|<=5 zH@aY1!CbgP$uD}3Ze&rTm9PzaRaCRb#gejRa*@~hj*I?V3t;jH)LhV1R`Fme?E)jS z@|*C8E;Ao=3xZCQiOd-^L*z915}bxi2c5ba`@75n7{59I+F&zUPD&f7&v}xN>AKtc zh^UoE!JZ5o&1)N=FVI1g%^QupDp|4z7$k=YvYzJ=(0xk zB!{6x#vJ77k;Hlo!rl{TPa1X|he-`3la6(Po8s0G<|{zdpP=rY2sV_b--3j{%x>6-KA zQR(cQrd{Ss&rP>pP_rBvpJ2+jT(j1%hxt-oX4E>P`oQhOoP`~ij9+y zrWnBzrZ4u)yu586dYjVPLWTbmCmp)LRC$Qx`Rtr9mVZ#wXt-#kQNpB8wF+tkpa=8( z*04A)d>ZsUi5WXNdFU*?k6fWOJ8N$1;-f3&YMx)V)b$wQff|~M3f7?B8G?AN?f#~* zF#Nox#<#4}btNxnv!raLZKGhpJ-@=!=djbFmJX1Ta^dUE-mn0T0^3;?)j44A?m&QN z8e}j*R@xO}i^-woQ(*u_!3$6>QZM1AyT!8h!`)2(lv@3F>9v#Z z95WfBT{6>&_!^g)GDY6mIXZcH{aT=I&W>)3EC?rh?Ep2@uD!Oa403_!8z_cd5eL4y zu{#000LT%LN%2$h+8?-*BKJVC;oC%M4+r+?^FHcnz~%ptTD!78ikbq2rMSN^K^P>3 zAdfL41st8e54m~!RG=uS^?VY}0c2HSLGD0c>FwEm*cn-jl^OpG%-i(Qe~EXTvl|Ac+vN6bWwO0rhQ(OX{8ymniu-5hG@8 zZ&7!}!TrqtoW_DLCS;+}8t`Ul?gVhIIyiubnQ0qlW3r*pzmq5iz7Fz+^M|VVKfJb= zPOq$FDcagv`X7Gr?`@(%LIkx4k@ypXJn~;7R?r5^*~>LP7J<%YQcTX%twI` zIOm9mDhZgM8ZU9ofP|WoYV9NMaC<*^*sy&~XnX#si~Ks0Ddh&P2;S%wGfs zG+AetwuAvl`M+#mIS(8LIP&oyG1~^8cwlC|=)H&s0e=6tiD0&af1Ai}6T#%*`t3!2dy${UT>q7u{PrTh11(HK zj>O-A*6%o!qSRhNGj3>Z(rMwe{*lPxpiN z#~ibt(>W}ccA~fnxtEq8DJ8p5Y>1q9bhKZ)cEzLMvC+@+<73~g6~@)3r2jNr%)RuM zv|mjKrf!`K-)^k>r<-g3RX24ZNlR}Fcd2y#SZO&`S~4BNPZd0WdTda6)G&}1_oPCh zIyI73WCGtCI$GYKT3s%hmEV$)U)!e}Q2Q|AWa`r`jM70BP?tV9Od7Y{O?y5O@wh-l zpY)XKIk;8MA6v20W}$?~FMEIRQ(E6mO!64-r7LMmHj~CmLIE*s<*aLEP7X4MHfFnD z%X#QT5>>D~j&|RE+dpw#armabHv0JrM$d&mKwtXapWtO5Sh}+o#qBs$#~wW9 z{fGBmyq$&7TZrS@i2UOa{_su4a(Ya{=uTVg45-#Y=a=TG@lIE&m+h?^BxI}5V z-gK{7uDDSY+FKN@x{`9}_+Zkfr(w^`HE)33l^jC4>v3@+IE&qd7Zw1^RzH-JF3o8!WhvG zqT0^)HB`;3B>dC`50xT&F{*l{-SL(qhw2 zt*y3v^9vrC#Q1~73JHvOF$yhM_$s9vxx&z`1cyv6|}cU4g; z>P%L=pd+yMT3iUSue~o~8*Ye2fUTHj(~bwf0;p#a!8lI(vtuT2e=DEjNlYzs%Kogk&vUFfwwhev5sr5UPM33+#<qM?Y6f+A=#MwcT3>*57n#kJRT zg=lz%>m@vrs$KUMNTqkE-)HOb3Zu%LCm|B!PZlerFf##3EK*8n<^0h1@TZnp_zE=e zGsQd`k#XnLgyV_i<(&0Q2P#LTu%CliVP0Yf4Z#XFULTso@h`H zch(YQN^$2czBNp)$$v3PPyZ;_tz<1HO{aPdXAWMtQ8>M{+Gz)DE6Hy_ZkKYUMv(9;3!71qjnYtQ#WY2xGVuvt$N#i z9PqtEV!b?1%jL1aNf8%bU4h}Z(w`Obe8k^7IY%9yB-#$049nPuQgn{;Onw7UdK3Vn70rM07&xms4Z^Jx{?EGXK z%VWjISfk*LQ6yF}ne-_fcd*H?N{nl>37(o^DBD4_pnydRT+Z>zG9_N89$$or23MZ+ zeETYQ8sOp-N$2H{>J0eIYv_&G=XPMI&_a`!h{FWc;yo=cffR+ztN4k?%F1$gMa!-3 zn~$capyIV}b}R4J$Fes$tM#4{|M9=A`+xn5@(Ynhx{qTX+bBFeu7*95v^V!});nE) z$S+i`J=XtlUZci&Kr5^a6FhPvB{ulDgYOm8C4YuJhP_0)NA9mhTy_=VPVNpBRfQKi z!e6ZCl}&X;VUT1b#-X3a3qtVKN3;3iwCrwsD^53-vIYx>RtKrHu47f#Ga1GQhW_)T z6jsp-5zFo9THNtr-&#BoFm}w$&0|cTj7b+4n%xR|a1+EN4&(Z=-_tPZD*?DBcwScA zpl5A9A*Z!(*DH~Exj4h2K+{#?jb-I-#uV8Ei0LIf?-Xn^V~x=Q#v8ks_%F)GiBsVE z=ziOcz$C+qGpd(P9EN%%9w2cJD-=~#BXP)uV@^$dL#d^kiSI_grFrM6ikYobYA0^h z!6~(S4u-V##8(3?cI(P~dCSOq^x#2w>KQnp?Vr9>wo$9+yFv3smrj3#>gb zk?;YIH?az|?}nwv?AxI4V5G4piwqo#pZ5zVbgc;Lx!s;E@#9<@2gkr{H=~g@W&XGG z*_b7E0CJ?5D9t=jzRHo;TdRhh>{R+({hM`JKe;$QqjU8|TN5J@4)O>C z{nY5FjkQ+?PnVj@HVp7c@gVKl$$OY=du&_>3jhLeS~=t>=OzN`LnL^y9QNXsLbh5g zea87C^5ZNHyukKXF3M&sXvHSB5DP}Xq;(!$gDd*V|#nvGDl4}r!;9eU^x9`p*>JwXYO=VGw zTEFXMeL8|^UmYTj27TDVs+3|b&TPRkN{E2t&Op-%Yf8#AGGgy24J1 zkM^JOgbUGDa0oueC{YF`myE_E9bF>#qF0~*?ia}-6NOKha;C^wtrLlZXKvtxN7ILY zv)vTccvd*P?J<*q|GlwPc{nUICo_>8`;~6FmWl%F@MTV?SiAje0%^ zq-3Ph1X@h^cTz+Q%azGk!J+avn!wPM=G|p{NW`VDxwKKDugZvY=jF5u{?ISUVH6o)1<8GM zZ}Ns7-Xg(b!Z>!RV7bgGJ*k?ne?~N*%vfBuWT6+ejbCP- zt_>0RigU@hz4bL=s&Q>SiD*_E4srLkBK4v19AQD%K;gXBrWb}MU{Nsm)ZF$PRklp-=H!~XqPtdTK0mVe)uX)0zys3OB*eT=m>_$`BJ|-%Ql`x#1rf*B zF_=+SEmqc?gXx_00RNiBHs$V(mqerO`FP4bE$49w)%Q%}b&FkO4uHYpttFe==cMl0 zM;vFC+?m9p+#eBO5gxn0pH!-EaS8R<*z>RQgQ`gP^&%2{P{MJBVS z_Gq{LZim}#5iE>;h?vg#<3FTpwhO0CguhC|o+|6@9+-!+)j6=fj6+T2^ZJmgpL1R^M+znYUS<&nt9@@Yh+YSXKBnlSgho)RO@$o9MUx_ z)bQF`pB}fbZtcI#E^uDgk(85z1x4Y+gqw#aOTNIC_|+dhVX!tJWz&heC?bRVF&0wO z`=vch!v!ZaI+WgM)(D#a@p}rS8(wyZoo1CF{m_S`;gw67i*IUkbb&{|)1bxkdn20V zkAt3U%f2{gkBiZM_@CQ`LlmsYwKAeNJp`;ynRi}S=83`&vO|>WZ>@0Pz-zTOQ6Ix@ zwtkiAcz<4u;JwIpFp|3!u{wO4tJjg)e6O~rz4AYJ>1{na5!;X9xWUX~A}tr0nJ%rr zN>}7ZD%cg+?YXeA@F3DO+@>>?@AV?`AV#!L?^a%i^k*=*ZSQ^29X<`UV5qW-5LF<}Md@q9g`B$F@8?eG>-ZS%anKobe;y%^c$P+|LTs3GXdH6+LSwR)g zjN}Wo=IJ|aH>a{2-3E`Gv7CQ-eZhX9`2&lD?ED{Z)ZMSXc#&6>rR;Ck8y@YOmX}qj zbrJuG{}lK5N}Vkvu#I`5getZZ+Dh+Q$|{R=v#SR4MTiajDrBqU`x@SKS+;W^Qy%F!b5rzyr# z_2ogACXKy^!)8v|d&8|>kp7ze{$=d9+0VH`WhMe{tbH`?a0a%bSdy4CGuQWStH6#l zRMm}HCN#;0r|n3TcA}hkR@`qXnhP%p=4q*3Vw=5kSCnqqoK&%x9Q=#Ms|z>2E? z?nQ&z&z?^ggAY72e#ZP!!%Qke`F(}xE!7;Sh_~0yJ)FPz=B6m!!>4MwdDRxAv*ho#AjR8*mS5A4^c;s7KTR)9*4rgMYo2~Ek zQ`HVpzIk8${*rZl3V|EHar8qVk&S=Y21 zedh_ezywE@m0G8G3Gr%UXK8$C$}ax06ulIP5i4zx6;h&om8-Dgyn@|Oy<=|7aRNz3(`3VeU-=C8W9bHupt zKN{g56{I`w5=BfRi;6h!C!TtT#?Nh;%x|fgH1sCZT{>6q_0f%mC>YYm@;;Q0;C&4d z!=1Yh)02e1JblX2VDeyXZC1i2uUc8tp3o3n3Q<>g`GA*To)DJ)Mzn^Kevk-=2o0r` z<|id3rA*Ja(kKcuDGk4r{$z+$6H|;Ij%f7IghR zEn^E~tB=08t1r30{Da*Oe^N01@`$3qdrVTE$nIakD|=x3u%-Pg$0PWx|4T@Wiqh>~ zrva$EFD;!I!EJv0ZEw+TGydzmHos^_-e$7s2*mZU*o1|VQJuWcGD)DXOyM>Mg&kMi z)j9ph8~ULEc>YOF-$d)flBI3TggO9o;a4|;cLV*q=z;p_g%#Wdg%Z>BMc>!u#2gf4 zW+Qdh%%%qeh-d=%L=;#ZUOmwQ=kH7V+ zLpw(-aS3cY`Xb78-H7&+34*y?kY4IRVb>WW$w@#I{O;LvmGT;4VMFn^Jho9rx% zW_msO!WMSPOi%UygSCnRv55Xxo#+imnb)H2tzSYz`RH1YDXK_)gRLFYFK*vq<&q`8 zhV_sYmJKzW=l$g{FR}azh&F3;OCjF1%UYvDjZ7qW;qZ%|-*0gW2wE#DDkfFmRAgml z3s8{Giuh$GFOir5j7F_jmFDHDxxT)uMmaHLN)5k(c7P>QP7ft-uN%A5{}cDVo>wUfEQwBm~b;T3;VacZKO1d8K|IW7_9B3)KhP z7NNiX=`=5~??3}-Ml!O7W}iSMT_TxTnX_AS@>a3w--U%U7n?&eh+1|GqH4&ZAO7k_ zO>+XzmZ_1>fx`*>$tCRU3a;R(Un9JKF4tb=a=K{XS)ju3VcB7mu52$Q-lfc=jJ#jB zb6j+QRbS7MV&=V&2~0{-P7*f7_E6)R63Wn+*hi$R+C@c22MRxghekx_oEP`jH_+03 z`|=kjD$y04t$61MKhicbGEy8Say3dazShMyEnLc)6e98pci|A{0keQnN^**AyOvw6_T{ctdp2t$~O=i0UmK`7QgceHWHMJ)f*-?7b_USoK@}c8nP+s;vYeneoA| z{)3>2C;`3H9m{jy&2h_UHl*aFB0fcn`IcsmObc9=pdrU*zc5*r=c(_}OHp~f>pzII zr^0~e-E!>Q*XkS|qWl1hTIu{{IhjfP^A|=w@-L+_e zMG4X+T_WAxz32|^2p$e-W8gO;0yuY%jXOZr|*KO;qDQQn=(W$Nj1AE!F& z+*tazhX*V48IGVWR5G3Vi=%mrj z0$0n{p>ye+ft&l|&O{!bM5B>%KJW8>^(pPL@xgq{PkKKe@dxYcWKlv>|K%$t?|O7P$FKhM_zQP+XNmSJOLEda zy)FwOFR!|e|AbKbMJPaGaIiNvsh`-DDP%pkv+3WaslK^A>Ajve-JgcS9op@6{f@~h z{cP<<{z|JOfDSe5jUbJ9Go!i`|2oRl!l>1Xx?kLw2O-51HT z-_Da3m$UnCD1>#ETD;3HrYf}*t4+tmHyT*rbjh!QBUJiv2|yY}d<@2QainEqQ)s$B zP%n0PE9T##tnER4FSBQ?evPJY6%MTD?YJPumTU=a0k#)1vS}sgI)83@*f8%I9IWDp zi~awxzK_89I~&SDwx^P`k|{FC`b`(1i{k{7juQI&I$AlJsBJpoWGJ+mnmY({rX4*V z=YLNH!2DVSAMYr6o%X=c`2uFoyN{IKJ7UiS5u~J~6h!6;+08mShn20agwId=(9GxV zEj8WKDrl{C1UCLzsS4>l`LGa$aeLj7&q2jzZ$7E|xH42Q_K(=lKbUTGb{(ArJW@yI ze#{$p0J6|*4R}_vS?RX)|NHHGEtFPK@r}cBh6?D?ko-Pk1EK;GK6mHr3 ze4}k;fZvvs4tkWzg6nhfF(cZEGiy}p@40f_|7A+ae^-2T1j;z2Su&f!c!QUa{@qL2RuKyDc&WnLI8WoM_=6t_E>F>@fkklsnAt!-fM1>|d3X zvUySy6Y0>1I6|7eVSiV51RlVprTD(yFSpt(`jM_QlcrEFDyzE#E~Z?IBZ#)M6E9@M$Q3 zOxQ+Q4XktN`ZXFtsf}m|dBqW^!kH1PtQQ#p;6}Kk`BW`@ywY6R#c8MfU{!1(_h40a z&AhKPLWmwq=$(NpeMl!=yMQA5^Etd4K|P4riG%1n6I_&6?XT9C2EZw(+nvn#cKuTS zwN#dye~KA0mH7bW#yZLt65h=?B*ro$J!@rJs{ZwJq{VER=qMWIvM#^6WWe+Ntg99Z zYQ`@dVtzecE;*oMrru~GiaNj=y=O;sSaiSgZFuZOzCOrgk2DJ0XSGMe_1_w%W)9Jg zfpoo=ViAMYS05^7w02n%O2UODBu_IFKl`)DfEc3`B4c#qN?FEX>xNf!QDu$mC%3X* zy#eKdF|v5>5Z=dy_qCOF&*gg2!u*eE`t3FQfHFtfLztuOUoS`QYm6tmF6P$)U_dq@ z^dX-!S;jv(l6j$OB0KpZ=DmCl^ZE^Vl)u<5_wo}fZM`6rfy~hrF=W(%Z#A<2^`j9& zxCQ)eP3p%ld+#HXmp6)mjOcp?x}R*NF?|XUK8&{AB}{&`K3#sbUhe?TuUc1cbmG(o zJ7=;mzp(OQi^#>9fnKxOiOpP?6+S#nH%K>WA7|cKPtuqrru?#Ep5CludRwGoll=Y4 zx_UXiRK5>uoB8OIsnhuLujN)vvqb561FU@AOfgX+?W>svK_5)Vgb1q{Bc4y4MQAG( z#V{>Ad{*ke?+25{5+o_Cel>WC8^PmxgLGk`pX~^V)i>S(+QwUq=4fK47|SD20Ioa_ zKg33t1q>T3xdqs#->yYNN98RBk(;=bsDb(2xXS6|NZS0vSrI~5crv0!Uw^*`r~an; z{mF`>VMNhX*~e@Srw-o}M7K7)n$Xu`E*m}PW3e6qUf~Kvo3FA~b~{$s7EcJ6YV>fi zL$O;6TqvAa9P$sh+IVqlcwbHQ?Mg>E)`MU^27Z9vMl@xrB6XuZqK+L#ZA>-0#ZJ#jhC_I(p9U~q1au*_xm?G#^NYTu7O zi6;&0odZ^>bXEK|6Eq>7cYCFOURR<`6`q4w0GJ#enb2TF5r|yOCA=3+5JrqVJ*5I+ zw)juOO-D5JPa%j+9>0 zdL>dE8U!NYfsZ=VwW_RJ{+@OxF^z^NImYX8issambfS1auNk_uDdAwehelZq{8N4- zq^H=H{01?vezOU%uUkW0RiFOzGL%Xz0e*~PGIu+bDVi6E+wirOzWmo0<$ns+-@8U! zrM^gy?Z` z6KPVL8)=?>Q1N`TlHmoPH>`czE^z!-o%Y#s zqhnCM#DV1Ff+`vCh2&5U{dk?Ml-tx8l0qt*$^@Srr5p}omDN1$?qp&9k8J&n0pLN# z*MRmA|1;41#^P-gAXPAwhO ztCwYtKF=&>+iRcF&F!4RSXlQ#`MN?*24!=}62qs>bS%3kN>(7Z1J<3>Wv;;1yQ;C; z_0w>zJ7V;YEEzQy?7K`bs(gX}GRbjLy-b`MHePS!_94;S(lT=uq5~MWYJXl_hK&z7 zJBu)d@Y2tJ(t>&Nu<2F?mr>*0Dm*Oiu=#O;;X}o0=Cnwl%gy$mzNcNv5+sh%2FAwq z)A)+C47z`WCHjOVNXes^Jj9(2SrL=In%QIe2Uzb_l3NoS}4{zfe_Z|S3iVEF( zB43$iP)7t`60p&rZWY-73OdOV5ze&m5kBDlLp9-(3Bs-Z;M+6iRec+9^u%EPZ=QTV zgVMkDaYqc2RTfuL;xgRYYEA+wL9G?cU>^~DzBiM3ic&5V?f}9exvCQM@L{W zYkmkRzuHWdWq$ABOr?~I{rIM)NF(w#TSj6M#|G7Oqkfn|jJ&J_beH6DFIo%FDTd8T zK=tdWMw0V&3G94fxz|a`POcQ)Q ztyR@f<4;*%v=7Oe9)&&cj^Tk->mOc0ixZqc&sfmyv~EU6vZdfw1NO%!mGe|5B3rj34j7SpK)$XRuLViEL-gdXA$+=z~Da%SI_$HFd%Dqc8YNl8ylNfSMqEwUzv&e+Y`pJ{sf+yvQjFF!T+tO@j|Wm z4ge8n;4-LD0Sj-l-5XZ)siRx?GtoSJriNDQh-qFRp+_er5qr?)sRtSWrt)ntE@R|x zsW@8imx$CeOTR0GeP89J^4DF4>+_Q4r&Qm#r>TSJ7F}*hsKp)t*mLwdj0|$i+S=OD z8cBTsg%%7f^H%iJp1RB&T(#!a>j1H!;OtK1(*O&POHV}d`$WKJZ759L{Zq!hz*2as ztrvWwvC+}puRUbKz3z9lX%QO+trtWbxUCQPT-GO)5h>V0UMppaqjA?wAnL3cg@GCN28lMRod`iyI^&6FNFNDVGs(H#o62wY4JZs00?;cn z0qz*pdZ$A7^9kk8RKcDraOo?)ckkZGEfyY8(u`vU>W1C{zmJxxEz{CWy38F84$gI> z)0I_#OA*?kLHuUZ9)Kc`0qD;i3}OS_-ExOEf7_C7HuGgP==#|$CwY!ETAhdG3d`=6 zmq{-}INTqWxR-{84HaTyR?t(NS!fMZ}Uczaa9yB02b$BuJ;3Q{I!&^K8# zi7?{*2a9qnMBW4T6@yQFiZ@B}=mzBmrd3BWJC0eiEA6SoOUg>c0p;SXIS>X>pHf}* zH~$6u?20XDxYU+jKRI-_J>FHVJ+*F3eQ34~UgkdI6&RpwJNDIOQ9mwHGIC+U`V z)(2T1#RJzcM6ecNnPK=|E~>Q^d_}cM9=E0k%-vhODh2A*GUV>9ZD{mK?j`s^e*7id zv(?3FZzlVP2Pc1e_0_b75!&~4(h{%!6eIbO&hqFre-xY`>U1edHS^Ob85D89w2s756VcADcJpePkhW%*zb8V=_sIiEk*@K4N!S9`60GOko;NG$DH?F+ar{oUp z$^Q7M;--_lIY1+QmNVf$bO`yl0MN;=-eAS|i4>e{QHIej=crbg4C+^hITh7j5K=po z@LI7iKzYzzkto$d%gy`?dN zL`l3FYqf=w&|HSqYm<+IIw|>`BJP^dyptoNjUd{9F}VR7^*iTi+&6td{bpcBfC!S0&>An;zDeEDB0+2m z8!CuDZGL>HAL6?)z!oh5VeA4};1ZdWzfAy0hx8PkLcraVcY0?ce;t76IX&W|T($wA z;>?bmx-ZQ4V}|6g5056DYH%RNK2Uy_yUS|b-bt1^^n|9aHFJ~|V&3XIXu7pJ*B4xS zPB%y9xI5`c@8jI@c(*BdK=ul<3jFdk0A${lK2uic2wytU3d#`TzbO*39g58utJEo- z$<0^w@t;Bp7O9qMH(30>)14!#9#*~_G8~K*8xz-)GRhwr^(+5E-`T+*&L^tEbySDF zJ}fg39Vq*=Pwql@c?Dn!hXW2X*VbQw3yCy=j893Z1R)Idhw-R@T36L!;K5h9;%uz_ zTzmOfudPEnrGwX!cwf1IP?mGo0s4wOBm_c_m}qkw7AV`O)^@?QTk7y^!kU2pyWASZbz>j)GZ6rK)bYg$Nz1a&82bMVv)`1KDM=asU1TUmSuG+r!2nCUXo=;h% z5{FJH=R{e<(+{ger88jC6;sXe>bjl3g9f0IG#hqwQX*jCW(Yt@ed8pcN+?}=mJdtz zFP2s4=;+VjrHNlC|k8i7{1VrkS5QHfOqX`#q-Cr!3(j# znEESvdNGo#IbZyPD=RqC-Iq#p-r)}#0RV4`?og){gd#5pi{hMzMkN7I4Pn7u+Fm+? z-stzE)v}A&L?Rk55rJ;ONI+Wt<=&KrahL6$y{>h<8_0x)WPJwwwQlJ5&oZ-Uj2oSqFKFJld)%hV6+Cl|Nng z+O}+J@fEf=uH&ZfRK0mo(_8XVbmY#yJ6jCq zFTAC!vTYxdJ}bXqasvNZmOyPKgmI5$zkFLK!leL(@&h1*Z`~I%{acK!6AjG}X%0N&&!RT_J>x0?aLA0b_~ z7}zYl1s|@KBWv9w+(-CSq6cQk?78X5X|WYLiml?Oj}G{|y;+f`xdr0bM!xGJ)$*=Q zUXW*wf#;?lyr!XnFYN76(K$ft%su?Pf!&Q%+;M~s{i_@BemVY{7x84J{OA}?b_s=Z zOzF-CIE%yl;ba}2ma|ogDR~3e!(^}vv5|hQ+hb;bbf74JmUg5ril!*j#2S)e1 z55GMDz&|f~GvPokWY-FB(^d2DM}egKAcGA9{~5oWXelY_QxmwOtbvp_q~P0>*CqON zon^kdP#Sz+J`hP5^ZT=lRwU92hH`@*tl1K`jec;(@(kBBK<>=K9WWoyS&shryFDr) zIo-?2|E0SZ>4n)u9yLHm4^Ok?Q%-951|gvUnS*%hoqVO+*-(6%0b?|6Ef4!%aT~&n z*U?t2nKJ^QdZnytuW>IutQqy^;U0c&@Hmgh`Aa*1+tqDe-24_qL{uQG@}@x8g6;l# zn-wS32|doVM^U_B_(_e zPXhQkkx^0lG8Z#s&W~KeAQzf;H#s2rwEq1Cx*UZFNHR&x>2lGz`Q})Js_iRCqDF!;8X=&J~$)^z~}{AmM^8W87CJH(arjB1mOdWpm};z?2Q@<2yi9CzG6NC^zueAL zJ`;FydFY_-JKM(UoQu7s;APTIm=Vu`atM4c9q-Z#P=FTox-`$)H(448cw$PCTM9DR z0u2iV*My*Uu&?mVpoxc@U8ZvqbP);5Be|fV03c_+O9kHtSVaep+WL0@q_!Z96u%3$ z`KjD!fOz-VapiZM*4GpqM^e}YfT{!bsT`6McQ|*LsAgTQdSFb65jCl=%)cB`u`b{5da2*{J(e*#vX`$jIAB^ly9MQU$ln|YwADd~kRSDD zqf$6b6)3`34Z1p|$or;amZCKQavs~;+l=1n5$Wx@61|al4>Vqn*OyJ6AJOxgCf5zu zN1MFV=lLr;p~KGfTjbhYdjOgB+w$qzoexbKkiURJKaP9&C{sb*MDb16B)rTf3*Q?r z*!^g*yCmwZ&zvVll@O#;4^U{fIn;4i-ByiMh|k1LpL(p7@e6ch@qSibR;WC55-*_w zAm!q;8=lc?mi^BJiGzKc&oxSafY$Ai+8ID#{QUTE-#EC9NH5+#22^IWs@Y)D4p#1;sik-)vCM$+Zahl%JxeKL@a;`@qV;IN0mDgqRP5O4TS`h%e{E(Ba*)YDbNguojJ+ZG)G?~>VfQEDwei7-N*vUz_xLc zt^b)|Zu1B01r5JJf6xmNhQR85i(Y+&Xi%f){f*NfMK$^%GLR{_?M*5ZbKvhiF(Uxv z8k=9-q_$pqi?o7O-g&~ua>@2}m9u)e;;A|ccjT7w@LXa0AjqWMbbUdD!{GoVFseGq zO+Mnw!5Exbp|x%+uidPZR8>{&I%AaIu`nIrS%Q@sFAv;y2tqjeU$Lyk6LocuZ)PV% z(KUt{Q#RD{5QsRU^LoX*##R88db7!b_sxZm(yzZQv7_lx+!MFey#10yzQMvhVICJ+ zQv8$wy?|HXRruMoXuCi7Jt-i{vp==;B1od&4bFG``L1kVrBH(ahWcH}OF<3O`RvLM zEFCKNcrazKMQd=Lrx$0OdXsx?e+vKXrg0*7^Sr>qQ0P9iz^zF*$N!QSbc^}*uVjtO zRI;-lmBok4=Md$3r+r$$OjZy(p2_bJobGcj=9yQSt<%~oT?%-9dB)^i!L>v_!N|y zKcjJ2Ty*&QNAQ_;Rx*OKnwyLLd|dQOI4iv;=mqd=W51 zrxQ3VX|H^@jw+S4qk5uf@;6trdZH}wrXUq;OyI=bHj1-VS-zqUp$!Mn*pYMBFXL_! z=Q#*?ZJM9~DOvE&otmqJ_mB=vB&#P(`r^d%b%ajwLgStPmrjq+ro?vgHaAQd_EC|; zOzs~Smw90?@($6hk#hee!`ViaR6%f!BfFU3%+XvmHl7F{)ctbRk)wH5C7EV2RKo0T z(y0IrF5w5VGg_p@p9!Pi4fTRI7d&S@mqTj#4(zp8+Di4iW{^9N9@yLlEll;?v(?e2 z8iBlK+0wvE)~o(jEd)xC4H(O_<@@u=3g2Xd7k9bSYCg}y!k=qG3WJo zU$zmzzQY}|sZ#QaWo3&9hcpy}TaNT(IaMp`O6{f|tdKzJTED9D$`1xrzJM zazK{-<{+stiwsoD7vE&q)VHC-sT)<7(sKjl2k_m=`2bx}1oK%K-6Fs=2c3C=T><}^dnZiAEUG&j#N*?0IohPQpdD@N_N4?*`b z6^*j}*Lh}S3k&@#B6m6ng8rk2OLf|x;Pv$!4Z$WDVON+4VzHCv2=ZK`mAYC9orYxY zN8a!-QRc8^$%#}jMrv`R>-kqbiTw$JLt&r_=Gl$F#y50STih;^xd`Pv-mFy%LsJiQ z-5i`h)Wu#jC6Dg7#|p+ZeE>%?s#iTCkss;3mc3A7b7SaWZ*i-?i2rHtv?7u(@dYGx z?#y6*UurX#$8|LnC3j0QP$jZY+4qk%=L!%NU@3Nn(Q);4fccTp5-U+^dWYYNGEn81 zy|Z{8R1+Fi_OfUw`n@nu!;Fxe!DbKjUx7>kdI~)V^7%48isV)M%Cy(xt@gUK_@%^D z*vI;(JmPDqxqD_CPOJH0(0w9=+?pY?&tE^$;Ebu+DusCZ&(HL^S#3((D#*C-W4q~$ z`O7YtAKAdxg4>~@-m_DLw@^UPFNl61QhZA-Ig>mrez7-)Jy@hDuUT|7rYc@@3`|1K zH&D6^Uo{Q zw~v}p`IMg2Bqrgoh!@(@VGoSm97?s8M!)_mSrUMZgiib}OvZIwO2x#|qNU~e=Od6e zW`mk4S}`4>3j9Zh@rT6UsUBqpUemtn+f?6+Jg_B_uq6itXI2zY(}KFc80 zer21cs$w!2>}MaCpWH74g*6<1@Ji~!{p?yW7$w(*6~qc2ra-JImP-I(d&-sjS|kQs zHu1ad393WD{?(kkzIfGdIod?*wE~0o_+lxByvQDkxy^rHELRTiUH57bwDmO$oI0ku zhd%~VhK4_tYH!IJirY96cR>bZJ}1gr9-Y9Sz=%Ssti8Hdike2^@O?p@f8$xP?><<={~^KdF)}W3 z-oGMeJ}(ymVyx25`R*oe_^;WHm6BlghEr-uHSs)R!HTF8T9ZM%L60*uEfjTU%k;d7 z{ncj>15EVk24y<@I&)jD3 zS@BNq0ojYLnXHB9K5pO*QtJsv#sf90*`mTs<#?cg#}m3a@Y32f+1F&yOojR3YBj8| zX9r(5YWY2Ua8E3(+-UG?isjzPqI=(fz(Qtswv4-h{Qr_~-Ck%;~cD>VJg z@Mva}N}~kZDXj?lOR%Ynq_-CEIq4zPbB5%BO#qURWV6`{OzWH{~4leo0xOtTG9!5LeZ|%OMko0tvrCC;>UOzaZaCn5`Kl@{KpO%o@E^EIMZOXV@GciWmH%aM_@P8X^Ux@%Z!*%P-zyVoAS^|XQ zmgK+Jf9qlf!*b0b$}enjAIv!4g-335`r{8M;&j~rDu)MIRm2i(se6pNJikN$+Bflz zUjRy^0g8m7mAa2WUS3I8ecp#xlxxa%cqB3J8Y70ok|ah`x+lnr6Wc z6R#=GArj^4m1%wkkcOOP}*hOEQ-+n2~xr9|SZ?0fp0 zsLeLu*iPCiU2vsrN*cmR2Tz*#h-L=3vzFOr2VEU7WT=+U`jO#{uMbC*Ff{6hjwtr2 z^2S{<8as`ZN-jMOZ~mrbdF$H(JvoLy2|4WwW~`dHjnJ22V;TX@Qg_4MX2EgvPVU7O zC_QEj#bL+4bT0Sc9RVqdG<2~csK7ZvjaT~v7gyq*<-FF$V)?pQ={{k!#_$Ek^HspUX7ho5-C~7a$RX{(I57h8!gvq3 zg#WVk^B=X44Qku^{h{H=0)V@YR$<{eNG|G*++r68Gr{dJR#7ZEM{_(n#)z6%uV3~V zF*|7?M4%zOI$fz^M3#k3f)l{Otz)PgITN^y6?k|&L5*N^yvwGEB)<0|DuFNc|8yFd z^YFC-jb7^c$|(|!SfZb7kbQsTk++}*2tO+U#Nuq8T7?Nj8pF)YZSXy2N<)&xcG+*4 zKeRFE%aO^tx_se&2N`u2>xBz22clzEF9)yoQfZhA7CQQJPP+_O;cg|iz*B7^^M}IBrEjzV_xd1x$dF@mn+ud zH;=sKilh(>Y}yk*8i7)EZIecqu6E$L`25-h>0;PMpR@g)Dvc_^T!!mUp!1)}g&mSS zRqG@IP0DLfQr&=hnxd$8w;a)eZa;-KsHOj~{7Qq<3!}_+09G<=+O);7SEXc|u4!U! z&mA~>gkGc9iQF3YC0jO?@hK^x(?4S~fWf=R?6FnjcgZ*IcldpVJlr=x`p@tjPwJS{ z$y$%ym^3H%!>?jL1rqNpiQs0{?>{_M8&wnFz74LS@te4?M5B?MN_2A~?~vxa6R}Y@ z-QATJ`DQ8>asOlk^(vUJ`g<(rQQoK=`8cEgVNMN= zd1;HF$Bi!r0YxA`x>Uk8gHdCF@kF`lF6(eXYkyk|F)5a>wHy~SMYtQ0a?bM-qn zY|s9y`8|{?5KOf7w={EYvOC7|5$Z;zR%Q@{O3L>))2KeYTeHqd-lv3=->qAUsc|iW zY$2Jv%|s9O{KM=@XYfG4!TkcwnbYAe2{h<>HB9g<9gd4Khs0?=A4f{#!zUDP1ch|2 zI2rEk7B@N3FO=wt!tGm=o+;dGl*HyMF9I@lAV19GwMoqUb_)#CHfUA#_X?-44(>4M~h8`y;2n4+@>UmjlU&*l%AmOtw!F2hYXIo3zo zF9mCD0g~;HtLeF%F=GHKh}xp+?9Aa*x8-)yPUaA&uI(iX?v~~(gQo$4ZQ9seC4<4| z4qkjuE)KKtcivp=N(^duknxT%h6+xLyAq_sHn1NBF{@|zp8@)J9S;DhK2!GfM%u%1 zm@1H6VE`$wb}lu7NndGS?G=_d5jbX8{qs=Tcbezc&T=W6E^EHea5azOQeq*$wttDs z9KJLp>(qaEgKMF{_LVCCRo4b9J=I_$C)1iAg{CaII*6c@>vG8#qpn;(0$WRf{0l3S z83lVJ-A6plCh5lb>(X9UkrVk!ZtK3? zrGWa@+E$Vc(jsV!{tkH0fw-kVKn60VP%Gwg!k?*FLFVTzEp}f9`k!N!u~pLtm0PV} z`h!uu!}%K1f}HoNwa5tXENMU(Y4H-V42jiH;zSG)Oxnagc%Q&R;gYxO&Uo(0(hH2< zzoq(y9f6pvTNT1~y*+8Iz_{a44a|xnJ*B6D@S{ujnh=Ss;&yWy~uKreAYi|#y`vz3& zL8pO^LA7s9$}f2Y%mwKFd^(2Teh|ws8e0)6H(}@HRkL~A`(L{N)Dt-7nL2Jp0 zzl%OWRyLXWtb}LZ$!5={iSS|TEgB{X-&^;iRwVgd8r){3+AAa zPM`uRI{9yKaq~X%Rs0+-NkE%&CUMmn7eQ#fbWTVi zwOWTaSwOkVQREoR^nyvd(O!g@%YNev6R0na!Fe%&>w4aKu?J`--HO0i=>QCo)^AAPn!)5i9^l(f#HBx*8`vk0KykH+fIR&h4}%7R~O!?`uZy$* z2CJnT7J=Kn@`IbDR#HKO z5kZaD6$=ni|0{PZzg-ggggZ#vxSpa-izk3h7YovS4%LTWa zX!xWlgqwyFPMLcB>=J^jNOu|s6eOM+NOjazebZV7`%#*mHhm@O*cx(pezEtbD+t*0 z;EPb18AAuV>O05oUelNlM%j#_78T7AZyHM)rYP1~4M(JJWXdHZFI9 zrr>j+-0SjgZ>l(zmu>+Yh?NeGQ#J(%TF|@x0s~lKN!HExRX4)H5*ev4Ky1tV3)@U= z_RYqgf{NFCB+?JyX9>+?z|JiBEg-2d?_n{P{nJhRFn$xQiDuPp73j=Llg+nm_<=Iw z0zy6HT3!inz^}@i`W7=m@e$)4-#>3nvBCb-5F>IqOi}Oqs88y}Zg-MZK&*!n*G*Se zC|-SWQij}9qzjW^#Vus!(eKE60rYqr2TikSWx2lU0*s9!B&?JRu|4BQGABActQAER z$l%Ofv)Z#!@sDXyWwI)6kY!pI)wNPfvW#eX4Ii2$G`+2p&QAkPA-74Ch%T0VR4crB zdD8c}y<8h%P`jk1G04qfb7OHLegMTT0jGKaksQ6`lq)a^^b-|=(y#oH~ENCL2v#nE4&SEDlV&4)VA_th~rcKVb?{j4#70bTer+YaMn%OhQkVx%20 zM|;@l&oh}wfq2(#Sl=I>39M?Ii9MZ&we-gSa>nkjk|?;`v1vY+M6*n_uNjB!)=*vW zVCL8l%jh`m?IG6+GCmG+(Etn1JB&%Q?$?@b7#sw6KijCAKV5suz)cx9k^1AVWFiOF zE&!=VXJh38703-Q0@i+q9~hUzdSQ-11O^u^Pa|7wT#6YS1ZF$47EaG=uTMAWtR10U zhH7=B!;2Y| zw;Q;g^f`rqTkd_4Ef5N~%wNg&5wv{ZBM|wf#pq-p2DEkwNB|p$&`56^Sm>72kP<<<1>9tI z-70)_BiF8`lJ(bY1v#i4J?7*axTq;#0lUf;)}(WAzO%obTjHc^X$qvkBm{`*)ha## z4rh$;2L=s~<3Sd#h~nEmK@|;0HRea4aKuoXe=#ii4W?D7S ziz9A)FP`xjk;8`Aw8Un+BF25%&-OC7`e??f6G!>)$#Ic@_^ajl(vsi6ek{V^?Mdb?5bAg`q;ubiH&T zH+}p!2po!rPm&tB+zFVgadUh=g_ZJ^uUI{D@5(Kpv}HSnUl)tn>Mri58#{tMxvtPO zD&j&wsKAX$G20RGDKMDS*H*2uz&b#Xgy;=ax1bZMjjok{gHTFKtTfX&s(n4692IQP zEwM+vP9BG?rN1~ZfAj!|dwLWe6*Ib%?qmC-$JXCMU~**_%9tY4(FeS5h5rl+X`%fFLGF zk)`h2y4PI%>JiJpk2q~gosl$nsWsy5usrP_ICQl<=10(C+wV_(wg-$M;z5hpW&_AP z-+{*|#Ewta`zCHN8(t5l@T-%0Ug=h{qYNBrrduyIZP5D61c2~#-6~r%c@aa?)yA@= zJ8!J6HxjLp57tfa7if;#IsOQi8}|U#-FY5Rx^8G>x|M}fi%GevW`Vfc`eqF(RZvimnv0mH%S~hDGQvkfN zmJHrLGV*`PkAKr}nyOi5JmqN(*%ABuAbFBQw7+X(Q-`5zPW%1G2fRjhJfQw8;KM9* zi5AaMIIlMtn|6ys`+@bJE|^;$=U>oreF$>Uwc z=8Ga^($H+_#EtmHDJWpCmx6%I?XoyvudOn?Cy-i0qtZ+s8m`Mu`eM8l-z;l@C%9dh zU{kf#c%3Qm5}ok0MEK2YY%j z8?dig>+Q~WCQfg)U_3||qy?UVhDBb$sLcr=2!M{a#D+P*$rqEb%Sbml)u;yFw?AeItQDrxS{j?O>DpMSF3Kedvg6>0Mg1a z#+LzHh6g8m#6|D@4&OSj8YcA$lXQ%EbsEe9-WUJf?$eg16&j$iXDP!vxw)oqdo)2f zoi><(CDAoDyH0WOL&Lacy12NhdIQ6p^d_zO0SCr@%ioTQ7prN9{B$p?^p8GCO2^L6 zI@iy(>nYQ_)tk;%72cM;k4R?;<9s*^sDC_&dR_HGmHt3c?FiBJf3Ww~aaC?<8?YiA z5G0gR8c~r3=|+)|knR>KiA^_bQ2}X{Zj|osl$MlclhWO>feo8)ac1V7GxMG^@BaS% zzVR=Ah|ja0weEG@eceafCOYP4him$1*O@iX;x6kw;Kw&z0JqHa+Ns1m625xif$wWAlU=N#+m`W8Q} zNIq$P{?!_2%jy92B+KYWS|+T)W|y&MNo%wP=fGxI1Vs@a`5`$2^N^LcL15_i4zVhv zILMBDXZdRv?}{b#=)o@!r&RYRH(iH+ya))xPrB^7eddx}DapJ|Yn3!~AKJ9roN)W% z>Bq+oou+gzyb(sJvl4ZRHE4GF+LCcrGrQ5Xd?YyWypV#>7FJWo$~zOGV@{kzE|Znp ziu1Bw*5bMRA~I_J9^7{{(B_3}eLJtxFuDBDcVa*X@4hyszY8krD+vZBXh>SvDBZHT zVP2k~g9Nlk%T9GPIMYZ9eTCobn(S}#xk1_H)UdwmWV4v{IW&`ASm4D~v2Vx0S*s4D z{$WR%sAQ;>OPbJGXQ}|K-rD80;L}w#!O0?{_X)g*LQgx*4Sp+LX@p!<8k1n*-)Wn- zK$&Syf}g*cX6+-F41GV8dC;<5UqmnsjAqk z3AJ4H?0(js)P+#Nm+Y7GKX&$F!CQs zNJwl~gFZV0jPs}_biYx)VuGpaT9LriGAG3-W|@tebspbk57!+{y!2hfI8_= zbY;r^-46z6#gAwXCg*54LEc6h(2U&qxNUqm_0T75dqy9j!0Wcf4eF>F`&WcqTn(`Xt`ZC;kS>$@>dol_m?yuHqn;8!a8 z(#Z!C`Rgf0LQ&XOvL!FmT-JVT?4qgw#~|T_e#oMlz}NnpXnS8o59qNzm_263Mi4%w zbu_;?JG$V`>}w$$kLbwg1W}g7Nb0Ry{FWbS`a$wm7N|^N5>?tqvAmEZIWOm&-y0(L znMAZ{L8}YHP*U>p?a}^3+7Fbj)~(-mK>ty_jJU4bPzdi$k{>|5ys~)3e0Rg_?i0EM z5O5WG3__Vs(e)bzwGo)#y)O{eLq#`ezguJ`nj$uzVm)_#fatfrRKh_5jsaB@JIFc6 zus^XJWnNP1V$BnH(rjr2JYm(?=cn}g%VEnQig5(iTDW7oS2dkW8oCAO+f3?t1RvFP zK%wZEzDGW{#}qJ3LS}KKBks_wR_*67MSlEQ@B#}$z=*u6G69v4(ap`zTzFg=bgS*t z<)Gd*Jqi3Ozm8NVLwIK%gFNFM*M(dDbNs@f4b7)RMbN{oy?cKlj(TYnI-64`vEZdh zUe|bkbA3obbZdJd0p^gV{*&!0|VR$$476m=Me9(=z$!-2;AaT?rF ztB%bW>JyxH8um(r+>jIckf>gJ4XzYrO`q+-*wL#X_!UHqBoO@+K_3O3U}j=&u>t=n z8wAXt9`xfvtT>hG9sg@+`GFBpkuvUaW>rk07z^uVmQM?x0fH0VQ0j>nEUyiVv8d4I zZr(^L+pG&!8=(P>r>{rKNcxQs2hoi(ZzOZN5wGqFW=x*EiDhb~(xk=xEezxWLy1B7 zqRFX~*1rmYHMi8BjEu-5(~>lYr0(3@ZXkd&vT&j8O1sY1!%vx~K$@kIO2rm>xH&RZ z7|jBuBIu=jTJ;@N^+km%2*6lgb0l{lgV-9fkTh>C?8OLQ$jXNsc%Qa0<&{=be89cR ztX%~&5-9dX+L60ZYW4YstJuZp0kDEFpr@y&gE>x2uwpO&b6|9a&iqzy`!1b@~LilN^ru89iRSzmu(_su4g+%d=}z{$}J$03rQZ*PZBS&bU%ra|79C;lcc;;2XuW&~+w0ju1G*dN zWV=5fDfF3*Xj|>vhm++Dy;Jn82lk)&+Ca4Jfw%7L?!I#QJD$=(7ndGGb0#bh?^hZeCG0iymPx99bJl_&_# z<=%D3y52f4*|oX*5#}pd5rtF6s#gFA&Fn9$5W?GDUVU}Gzp$U(bHzHO&v@TaB;s`z z6Qtozos{s)PI31AOJbYp8nq}Ez26&EQ;P^pZr?8fW~u&2Fd4VZ8GL6a(nekr_{Ue# zaBnTKnDfrmFg`zr@=LO08S1>ZAfK>sp$GE3OyAE&H1zW8R-V$BdApborUkGz=i{U8 z2Ezg3qt81+0UwIM!{>(&K@T8-f_Mr!)6N*Oz3t#XH~(Dy>!-({tU0eWa;n+`s#znW zN?0mE`|kT})RB&=s}W01>wTsVfK-v6W5DHb52z`Xi-Bg!!@c^oo$i@wkH{)^WlH(} zu2225+H3l&t?%fcaVERY2T%nKe10=)P``>_*QICs9b;SM8Bw)YH{PjtTbQ!Vhx(VE zxt}O&x9Zy-BF9@epFwHEZXEG|%#{>zV1Ks8RmaUQHci2`wD8v;t<|+*P-K#SI)Loa z;=FO^QnUL^kjiyk%x&?rR75lz)XghV)Us1+e>F`ilJvf%h|mpsP%WRzN^T-de8|zP z(zw6QWDjUae$(Sg!UmqM&*L}ar!DWE;Xopt_30Pvl0G<`KYe=3!4wOA)oPt*)GKcB z+t^;sMnQP(PQzs3F4y?kcSQM^0ikPTZZM@?>+St@%R=nBboFvtt|KV@Id7U7`v=hW z>>V%X<|(6wPiHgHv9gxHyZpEx7P-aM8mJ(8P)3vB#U&3d{Ae0)6@)(Tdu=DowOV=e zKU9reyQ!K4YL~vUb0z$myOf(X!HVi)>N0-zoTQ5#jp4>;eVcq;t#F8YKA)e34lBKC`iMhac;u`*-VAm}FlFio0)j=!u9urG z-|kE_wOADS{ag+c-K)BDhxU1WxY0QQg}j$d4tDWoL_Y&1TEbUx|1NTe*-wCLL%>8f z>kv4sfL$ToQKZ13Y13}!nUICq9-l%pzqjC{0h#We9@S?H9^3U&ZUT7e)7#Uv-5Z<+ z_(7k>v%RoxfniMP&G#@7x)LWNIyTi3HyLiv^Q+&Qk)dtBwT$`Xs>37Nz)ee-6xjBx znA|w}=+sOLVa}qzj7aZ~$V%xm?tUQ8o2$Q`EQLeLqAF^IUEyhRuyAn2w_>R97Va-h zn(;@X!hg?JUgasP*gxdHIXO^y#ex;>yn|Co_Ke$3={d_I@^^VAoFWnRwKCKC*JxM8 z%}YSWUL*T0-bF320VA*drf*V~k67k~)f0ZDp(DJbRe7Tys>5p&sX&rUJKbTO8d|Z^mzu{-P+d~qQOo$>3r^Xkk4x}US#Cm!Y{r%SnC2gx}}9g+CNmr zlQ=ir@^4%KY&(yYeuC=!G{CUKm{+TE56kh_+^vPUM#Bd5JKEY~Bk8iFZVMs25j1w7 z;$5UAV_xegP`cHd2GKu2;YFlBYnfgmGu1MFor`=tkGwCM3pQ^c|1Gw!SC$jDY~ZJ| z2Ii#Y7Z2KZg5=ZsajAqfN!YaVS^-Eq9h@>Zd1!R2vn-v@t#sG99V}*NCf{+z2rip* zyZG6H&oIYb*Z%b5*6ZCato~8nST|=2CFCqPF%UeEj(P@uN1*H<9-lQaC3#7u~;niMnrW za4G5mziy_ZHj8sEYPNi=p9LS>#wg_aeE)Gu7LCH;O%C@F0@0_*-`bzIMVap(Vpx4# zviNpqLH5PcCe{9y9<(b^MPnX+(bCb;NdN|@9mvB-_CHQ61rcwJx>X6bb$n<#!)NG4Dq;blp-Y{9 zraa=0BdaxAI8*f~PA6`^G#|^T>&)-0Y8|ezy#Rw8OcQGszmfo{M`pmp5^a#FOk)oM zDRxcQF&F1r?k5ngX&0LheA6s7mA4u%qJ5+gN5OCsGiHaZqeTeFkELfOc`}aX=@D*> z<%dO$MoC{UC%P|m_*48~a@r|B@+i<7nWtUOnB;mMpjl&OLDleL=u-wkUqixJq+`-8 zDTLI))Jvn24iaIKjnTsHM#&*Wg2hng(pVlyVlz_TcH`QJ4o#ft7djp{E_Od!*Q_;l;@+8M{UA@nvt8dYzddMbwSQc?-MId>L$a8=)u27ZuV>Fx%2{Z3o6 zFV4YnW(S?pU@=ImlU>>LNZBp>PxmP=qh}G;p74EYIL3vfVOmXHuU0AB$xk=CiBoz! z`|&C}W07awAXJOUw5Gb5Y`4(9L`Pe{BD@o@f0)gnS>>i@?0KHh-@bS+e2_s9O@MA- zVv9O;M|U%%jfs?W-BP8>7I9(1@b^KTDduPlZ{wbQLi3Z@xOa4q#fOMl4jS|7aCBh( zddHv6v~23c*>ruG^SMyAjk1_RU%S#m?`yMc%k|idOZmC7!upN)$xTpJ)*n{reU$6= zvxs7U(lnh`_mkxQi(^qxpmd=0a*(6DL%#pw@N$}pu=LuZw7gF|wJ1WSW?4lvctjyT zo%H{lk5@|~eLaWknGTs2hi^{Ttiz_etR+Dfm5<|0=0T&-S*4-T z(P4fR7-9Oq2e2Btpp!Y9J>0M|NICCL=UQ6t$>vOLP;I;*&NZ~C&d*KKrF1!85#Ia4 z+fW1@ujRs+5u82k^XwW3^FB;db0iHlv@n<&T&8q=1gX?wVqW^7?;A~GV>=f5x) zN|P@(Qv}isS0M#Zn(d1p?>S7*V{(vf=5ZMU7_zkt5d%ykN>N&e0PPf(~)kMdd+e*b&s+3!&SHF(n2Ujc?uBoA$vXVWf4-VvR^BR z_7S`Oq6rG06TDBOp4o_Nb#=t>LiB||9`)jTGtqB@NMiJaU?i)bnLsbU41-rvKI~Md++P^69U1n!k?oq4rhu)Qa>!ur9e(jwnV}0ns3s#?toS#*U8D5(Z>F=9WIH3GZXT8 z!b0RTk-^tw--{+>#j|NIwdLrZAD@PLj+ae@l!yJ*!TmvSQ_jGMWB;PCcx^J{iwq~X zlN(5lXGo(?eZ_W4nGQcsqWhr%G^qOh{Ms1^M)sa!zvoJ^IVt zp%WPIPLFlh%Ck3@A8r!P7hawf9w^;nt6BYaSJd0HQP$mC!z9JH1`5po6z=5by2Uk|uQpqidA~(h3j-xnv<>Q@O-g1^IaYX-UDI3a5wut9IF6i6~hp_f{@X zqWCtes+BazrheLwGhxgsv$KcfRRSXM)%Ept>JZzFoV1^>;%4B0lp`2~ri2?#+Su9w z?GzoxQrGwDeTK=A0pdpF+{NAnQ$c@=gM?uCq>kWx9*zM~p*tL=G^?15h>%wnREe<# z$r-z~^yqd_%6kWt+3)?oH4YxXfA3P;*zv9~mYYdF?0mnn zjt|+<#@3rLx791P%`_(92F7Aydvo*fw1dRfzD4*BV1dvqGgq>js^Y+hrL$kff|D_^ zLDkQ048Mb^NRGiof6DEP5*jT9o^l8om zmsLcw)nSt|cD=C9$ZHkk>gwu8K!e>{V$wYidP4d_)bliNCnhFxc%2>ct!JgqmkqrP z;<~Vl)93ekFh;~fp}9|BM6!QA;so#DPHtdrm%j_&(mNG#Lgqgiu%)AId#sA$Juui18v@<=pjl7peU5 zESRCJ%IGxX-!&fXkx`{X(J^COJ4BF{Q_R`+stuZ3-zC_Gak z2P;}Qcad81vD{?3b7fg&t6%R1A``|6?cIWTOL<$*HGk%HdHzpT|CcA}BMfG`PC1kc zPu$0!#=V;lnX1}5mG&d-Q0U3*oX%B*QH>bB)?me~6POfS;Lq$08}nYRi}C8rtGjdh z(Nl|t9e-F@@S9Qfg_|_14io;Jj5=MOExXfde+ZPz`y8LQ<7|oCxM!Th?H9X;^J{V* zbg6FZ5{Iw3sZ1F?Nfd?NaHjdzOFP|}zG38kMEjC+rg#S8f``F4w)unB4=K?e8{YgG ziZSnP`n^szs{Tyr?fh=Zw|QdmbxMLKsz|c+?d>i!-~55`!Mj^q*46SbvlJMfvq}sw zA$`}diBXy5JRb1YcJ8IE-+s2e81q&sJWzz;hXxPYLMz|qIKN%%0e(oeRDZyYuvL`&KptPB>92qjXG2cGjPW|y%Ji{=Pl!BLNQvnJUa1_jm}B>d#VO0wqmX&cP5#1 ztDNvXbKOU_?R8FvNlEcc7-J{NCkW6!>W?8>D7j|( zWYcP+M?AVWJ}$LBoz8&A`_-|oI0KGeMw5y2@@r}}L06H%unUGgfh{Gn*>nBpGtl7H)A=Zrx|?S>4ZK zvm>NjtnP;AbLP`h*K-&oojzz}Tzt{1@*fxWKNiVd2{6xFsab)JG%C}BGMzDa)u`L3 zrr~|Y*JI@@*2{!h>Jy9Inj6X4eTesylV;(P_nAR&bESaq&0IDs{;Civl=gv!eFCl7 zmu~9DlA)#oSqbaI&cV7yZjCp^y|t4+teiEv-2W06{sj(iC5u^{I9vA%-gz9RIX-l- z`PfNNd%zwAc=_97*s;MfQr{-w#md`|d!>?wDq~PYp0p@UsTlA zdW_>QTtjW$z;!WH0EQo2)#~FY+eqZM(p=8<^^%CM&rrB1e^UBpZ2Eq;lernqCs2ykm+FuH<7gq|a)xdFHUN(uK%!?kDOW zT?8P_FzmM1@bomLo}M0^yBsFD_jRLzIbLUU)Yjmq0XE&$%cTlVBqn<)ATa4t6-w`H zkRpV(XpVvC>RmWmmnlVPibljTW+WtBOVb)47{z9~9F z%q$W;&g(`x!5uqVi^E@8T~rDE92;)n_sA%GurSBWAQpo+5Pm_f!^>*pd5&4^+5N?J zdvjs_p?~9j?ON^oLFA?zw;;lPr6I@isI6n`CKhR^da{{TDUpEXj#bc^Ra$M*OwvHk zi#94=3>+2MU;{$~0s(1T9>wJ-FnT7m(-c1M>tFXkTTo|zrF&xu-|t)n5}Zv&dttZ75dHe8l$YS*rn59kD{3mj#v;XI+6i^h$E$O4c0LYy zqe1ls(h=`!%H+fK#?Zq>=X}Gnahfd|`Bn6cOG+&GL1tRQ30UPZ+3w=rr z(Or=&!y44sxWZ}IwD|tj%wg($)eVK7oy@mJ8ZH`+ zaOWe17t@vUZVIr@53okRGe4_|bq=j-WlZYa`N94gv(aJ7RW)uVR9Nzs5OOBw9l8>W zxDE)Rz{}I;m4f)R87Xtq zvQVnNt6aWcHSYV!TC&&C(D9D3pT%3^ z*a|{XkI~Tr9eAg!)-JdLsH1{{0%;$vuVvHM$cwtD>(*b%PG;~~^kwgzzkeBEq7&5$ ziN3r~r3beaF|i~2%5K0L@8=GYWRl}(-*-N8IN#Cg7+iCtf2qB?@~%Vb^1QIUV|$e{=0DYnkti z#ralj>>yO^tt3=ESFAZ!B9P2eU0*AVPhpy@JZPKw5Cgk|J2>p#g4q>-ww+rgOZB!UR>SiwS47lBKdrI4yVE4)gSNPbT!;eV+c}tG1thE z&QWZ5T)bCzLm_kzm#}2*i=eUm!Mf(>5UZ+hc3M_eTGaB1Ap@}KSO{l@k(C@7C%>u5 z)8-rW{Q=cl(j30nrY$nMe%6QUc=%7e+~jo_n6Cc(F8MDS;pikMlEj$yVD# z%0oY%YhT|@>w1pHo?2G-JHI0smam>L<-qvN=Ss8ns%#GH#)XBLO}dut`0W$e?OWkq zA{Tq5VM7bW!#9$~A5AfA!U`AgPr*WEjmo_K+&D=#Rmqn$zL=Qa z!Bd|p15lLiHbMecX6fWbx&;%r=7PSy)%9&g>3r4qDE@c!H$dkU8P@AFMt;jKXBf;! z-L{kz7kg*THSA*BvtL#hAI~H8I`wWGnH#1e3E_Xjr9VV1{$VsE?FeCD*Opp*Cf6Z{ zdm8Kfr3(Ld!1tjg06HD5_3Mm8b^%dHugBdOA@)ME7Z=h8l{hk|66Y@FN4)avB0N-Wp}A29A)cOXy{@lGiFW z7d6)?I-!(7AfQon1&?WV8K@wZ8=|XpDr<{s?*&o9wPMu4&QAuIlUWa>Y(*^&o6nTJ ztjwnj7n^T8x;#elTZRL{y?6+BD&> zt#1eRg3GC@(UmDZlbbkhMPPp8A6x(*|Ku?K9R6-yf#+K#3zwmfzS((`WvtqhItWR& z9!Yjp6z{9p+olF!Vz(ilePA&86evwh2~#QEBu3_4PmB zga3_07DV?^IL?0^=Wf)u^m6{>`_+9! zKT=_op{NCeuK7H}0XtLU;a6b~`{nEQLghi>bQzC~lNMaG*2+BWH3U_uRr~?g5;^7J zp#Q+x1#?>!X>dEwalWF!?Z+qm#nQw zQUSa7(I1T3zuu>mWw0}rNTSsn(zGpy#oj|Tnx%H~-i;JO&r!ZiDVdt)&~QmMxrfsk1309G z+7yX;iNvGWBPbgi^}yJeA}=SK$e?&4cY~DGpp<#kMi*hVfslN%$^Fd;HxV~Z1|`4} z%g5pxmJu&^m__fRZOIsIH{UQ1f(@B=wB97_jjDs>RiJj*Z^dXaf^{`JZ5V9Q*T1oX zzCGimGveY`TY-7s`a=!JA6wQ%{0tNn3$L5 zT8#Me^zLMt`|OX&OYlBhLPcD1^i$@WeQ}L%RcMx#mQX~k1o_|8RTmg>d!w0) zpzwlc=->ram$ao!AJnwbCnx9oI&XaU7@Iir?_u>Db+ zo2t=oEj68vGDrVplm8u?a@1134wCV}JKrBJXfE=w{y~BnQE3(6?x;m z-eR*>SyM@>2=2RDI||1?!Y^3ugWj zNdNGD?gxr?bImJB=LzO_CkgZ@^|*)SM<$1fSeX@K6Dpr{<5oD8No<^rK8}=e`)S); z6~JLM_2KRi`y=wbqKLv|2q%qk%(DP&pKmy6mSYYIA~~xgs-h_Q21?+)!)8*6Tx?X; zD;(1r57BN&Jz?g}E*K^yvDz^Y8$BLf-CFBy@mnbUG?#WWd>{0LF$j84%z}*;7)?(CBR+pd3JyB9* zR6QKMZoC;|r!{Wt87xZ;yWr}zH&_cvnwdGb;eV?+)5qd7Kp;KFijrv|_7F?;b=B^$ zgF>ifgRS1flbi(ttXtUR$0isE_{7j;c!<^b9hR-$(-NZRay~6*XMUjc>LUlt+FP_% zeuz19We}-+t+;M>f2&k*b+%Fv$k`>LG`sxdO|A{clcH3Hu_5#~Q6SvSeci|R}pwB5FZY-9TK z6Rh=Tq-M!PNaa(6?k|n5hz+b)r=F!*s{g-R!p-C00|jE%ybqjKF&iK_O*> zyVkajwzgs{|WlC0Z{IB3gSqZ0-X1VcOG9&I*w`+XXhlD2Zq)t0LJE*`_##{;?k?+{iM1 zv_cF!=QNA?eV4GYUM^$h&TztCTTcJb+={tq#FVUczY+@r_rlx9-ppF?UPmLQ`Bb2^ zrk#!gv35#6JFF?l{juESoOt*|gP4K^wjHNT}>enxE-UodhQ!7Ao^#_HHv&*R55?31$ufk-fZDiB^HLvb07J~=p~ z8F+3-I(;F&)O9I?2+50^1X(mbj=e9&s8B;EZmi)W({wVjl6oH9%ij2fbp@!4hk}+Q z2=CGoO4E^Gli=OUq(sH*CK5}@Y4QC5FQA?#oIP1p8yFc-yJn*Xav*W&V53{xl218R zXf;$a-{AZ~`TP}tiLojk0p-v|Pwg&%T!SE4qFA)QP6Bg_{Q(*{dWxNsLkW({6FR@w zP-oP(TpEr0HY@Y{%b{6sPMgMM(j|Wz@VFukG&2?<;hDYc%FU4q9+a{=iZ{`?iCN_+ z?fXL-LoAUy_^(EWEE&H-hd*EQLAy!e+KMdRy?_$2c`h+v80Z&GudqD$zs)xgJhVpRCI_p8O$?Kb zd0o)&9qddI%ah0F=0nQ;ulaZp#RwTI*KOjOwPWmfs>#I&8BxPc9Z&+rNIJ!#xn`ef z$Agi|_G!^`Cn398l^dkB3U9@5u_@dJ>-ZMVRTidj4D{dUkG)3ek+++DbrLa{lC+2$ z8%}3jPqwAz_n6JcM+VAA$f)jIeGINK`Fs|$;+5*|$X-dJz@+vzEaz78K*=@V{0w{$ z)+&@e#1uTXdiz85!BD_;(92p^y-mf*U@D# zvfeYI^!UG-rx?I5W>z%ILJ3HJzkiS4fo0uxe@cn^vV5+RnZx&TP`Yrx>%Kr5;P!ve zSHwg~2(G|Dkp@I9(472GyEa!J?{go7a!J0D4*UGyiumsrhe#TPq{R}yqMo8j!2z1U)WrV3x8%RT^;d?_zhc^df$P7zdG;O zPc>bPEWfC@zLs$^L+S4a3M?GCdm70hug!^C?jQOJ{B9Tee7>{WC8|6aD*lfs!@qh3 zE%!f3G7~XVkZ243BOP&wlLpsW^ZG}mY#0RQjkXTUqyF@pQW!A?RsUIT*0hV`I?H|D z9{RVp1cCy6&9oUs(m(wOf^j+@E=v2%+m6SbJzf9!ih5${o!UVJ+uuFYEL>peO{|*b z|EW78riSh47#~;2D5#$LH;MOY#&*3_8!u4e`2C&ii@+iifBDA$k5d0n>x^9)EfFLr zCARqf@p|1R1RFit@3wCi<<+a#ELr}sGyh^~i;dhoJS3|wS{G1z|Myov!vU{e-B#lN z+pE*AUfpG3_UEtu*K_#&cT)C23XQ1|zx>b7>d&5Y3lt>HeX^fGInP3n`sA?=WzFV4v z91iV0d@IB&t%qwvm6dTOK)ACQdP<%{c&l&pg`pzGOD$W)^ATS*va+7{bGLtq3Kf(WjXA}G3Eu{Fc8V$nZWHGx289Af zKcC?L{Z3C1XXUACk17O_umm=`!VQ95!|Hdwz}qbK58{vuHZvvs>IJ&6OTOu~%~gs+ z*DPvB7nhDnHdwa)yrS@BPHiPJT42SXje!o5y4k;_ecWt5rMSYz>7@N3K zE)mwrBE87H#`7gM=3F1B}5_o?YRI)&N46n(o5$7L9p_ zWQ1-S+&O!*a@Knfa(N7~o7(HxaumF(piY^=1$VPG z)?F40qBE))a?LuFRaWA=LnCg*#~~U-^Hmj&3gmonb+nDQGNO=O3S^=gmkomGSiuEi zXGyNV#>{ehmzR$R@>Ovu6QQ_H#{|6PnP!uP)9q2Av?77ie~%VMGGFDDDk>^GhvPm= z&H~~LX=?lU1+NXASu#*4$t1MfbYriYNrW`oYre#Klx_aq8*}m7dLseXeAY=ovwI+Z z-Q^8H;xIhHwZAs&c?g;B;p`87k0+2-bKQABppd2ZT252u5WaY$*HPtyO7fX17Z1;N z5r$r(o}gzRA-bf@FECL#pdX4n08eq80y9wB0ns}0WQ|3XGW!kR@A+mSCR>!gH_Z5rLW*k(l#K-b$rnodVP>Q?4x`1EVt} zrKY`s2mPXrj}X$S@Rx=LvkqyZ+O=TfI^gebxW_qjzRW8{cH00}F6LlqCRA>3oToV! z;lXK%$!a2(B2U8kKd=aEYAKGB6N z^DH(s=Tzs?V_9`bfCsfstQS6s-D1MMy}Hesj6Y~v#Yvz>iH9%4Gv#J%teH$?7FAE* zn71^&)NJr{({-Kmy=YWS5_BoBofD&h-z2zu$0t#5CG+K;Y|tAI>w90qGv2xE0}jy+ zQLl^S3XAU?9Tef0XvkeUyf8rBO7loAhQK2BnPC}*vmjGG4?_0Fd7k+j;;e!ni&Eb_SWpoP+7su)-j7zPhJ zU}JC!(X)ZDd9NKWgT{71nB$9s;I4qKWE02FBV2^YD$kGC%y%|twq=4eATy??@5Ig8 z`01%{vx%-1#qc{d^YrV0@ZKJVxA!dPUN*F`w((n`kduDz2b{}ga?!9QON8lX&6B}Y z@YsGdmw&3*;exAd{LpY1^=YTnaaKpeVLF>c+)FpZ7mYD-!j{KP5`y*6!2tTL!_&{M z`?xr>1Y_Qp6HiqN(ibvpbR%&PN9UUEA1$t8m&@2|$V>!)^ z!_ZU}oC@AEVMpG{vR%{-ND~fR{XHW7|K<JZC0Hoc$Q0*RUNT&M8 zPd}etFi~i&$$uL>-*6ng;jFx{{b9839hYf04ewyWmwdFB>1zOt{kLKwOBR4&W)s2Z zvQ}E~h3n)$9_N+zO)S8@Ta6g^m@w0?DJ zgAJIita!)D$N=6TX?ADe^}JvJ3I&){r3N(53LO<-H(jzj9FV#+N>{2`F~dqlH+bkj z4Mb8liZ%OAz?@}!MtjAH=%u@q(8*$)HQp18^BC9f1DG8~m~uf{F!K+k8S)?ZjLBcg z%6eE?Y=7h?k6)O}9BWSv8aoQJ9}kz$Jv*?{9zjs)5J%U|4ZE%#;7^)1l7N`3oR8W@ ze4tjbQNc(KZf}bxdhH)7NK)?n4u=7q;#w=b4SOMbXJd^bgKQI5gf6(akz>AK7wwZ- zwKg$JO>ipFv2GQLVUHOA($CXQO<`eS4v*vQum-_8Zl?B`+_m!byWdh(f;y62J>zdc zPYOlqdY|B65y}5b3x63XxKqO!#b!VSXp1>t%@H_#f=`H}TkxX4k)ydtSRCwT4GHr+ zq$>tvTF%YrEppBVSbKYW-Ml1gfnfqeL+mHDY^?VokOOTS3k#R_9(mKlgN?5kYMm3j z_UG)58Khc&^z++RW<1|o-wyB~%@5w0Mw*{54@90e@bFf4q+hz5O(D-8?tSM?BnhVv z-V*f#I*~}luO7(kzNclaj}x@M8#_Y@NnRZv!{Xr-ecvfvE)=%i?l(C0LO|1XEi|1} z2%h3PhC$y^_dXNOHkl}hdz8+%6~lkpF~r2%Mm&o&;fb3&^BkH)df26<%n?J8`_tHr z+kJa^z`WYU%F{N8r#s0fVx>qT?9%^du@gvkW;bsXo}o*s8UDCKM;iJDlkaymsT$c7 zt{}vpC5%a7D;5kW#(y`rfV;H=1`QULM~JatRb`AkV+5LIuYp%ysm^hU-?YA-I!wPlxyH18QC`gyCtL{KO)jW}+J1}=O zpjjV(y3$u7OKv_nT(8u$v{LDKLhlyc_-D#8OA)w5*js!ftbT6|@Q6~WhktL0$PqVO z>T0lP>B@gG_ssX2&jhh%&1;J-9{p~RLlw9_R+h6d82IK>vVnDD?KafY>Ad?0|4@|Q zVg19&phsUwbRwT4XFI4AncuoRINe);J^r|4KUA0;2yHz5x-SmooJh`a$uHyGi1}hp zX^Eti6$QVM)Ial!im_-J5>#KkHvppyAjEzi*JnT98bIeboJgIKnMps$wnt7z_G&aw z>vXHH^6_%%6>A*;*BE|F(=>}P$(*#tlb6s_;XxBHe8P%;nmDZIUQL|gyz zR)^*Dn(a~L7dvKyh&WZ#9p2hUlGeJFHhM@ZevB7G+-_B}r&IEU{A)*^jTgs3*(#ZM za3i?IrTAWw;R6GNvhfgHWNjj;PNi{s%i~Z6>)C@zz#rche2??^q+H}Qtj|$d28ai2Z|E z>QL2LNMXK5X>BZOmYP)-gVH6A2;mg zKV!Dr9UOO4YalX1`?kk_j>k3b#B!ME23usYsfG_({2ZS69H!0-kUWORV(Jq#Y;t;t zJU(vXTRot1QJ|tGEL{jzeF3PK(z%t#o!p%Zb}U}-uvjM+&mO+GNVVNw6LB`XQ0-@7 z*>W?GOXOpHq;OX-uVYdvQN4h?I{CEH%z!+;WJ8H@O@NDsT-b@L+dNn|CL;*4vl@C~ zc57#+H_+aDD+elATSHebnLd_z4_5waYJ3jC@0QaoeUfP@T`lhbkB>75a$aI5>d%C? zkyn(Nh_0|MjU9a*U)kPFWvT7UJl+;H9xSTl){7sq>^|8PdQr%dz9~@n4^z-j+|YG4 z5>G0G>2>m&{07@LQo?fwF&i}L?X80h3RFEhl-;Kb<+~rszZ}B{=I1PYtJICaWOT07 zg2JIlRw}Qu9e+iOc%s1573k^8^n}pG{D$D=QDPNhyLRCReR6PSk~<@Qb|h~Sr`gN8 z#fNMG8ue#tcB5Kmk)-&iTMcEjYRjR5+aIa*;VpHz3PO9&t!C<~8$;JHS2B~m-RsUb zqdPYA;Wb7#JJK5D8$Z3gsC9Z#z;L%b)gCaXEv__$Ce6<5AIcU`>muG)9Zy&q4}6n2 zWwRV|UyF7EMwbC zUf&&-UJM)tWXEJ}xcC3V)mukJ^@h>H2L>3rl#&J!B!}(>6(yAvfdK?0q&tU_kPsvW z=?3X;7`lc=x)G$i^PAtj>wY)>X4X0DEY|xz&$IWl_g;1DRI?3Dwrdsjz27ymd%~n* z+;%4D*iLWoBi(L(zV{-5WkmRKJNYz!r;0wS_0EhZx~}E^YoK-uDkgiqD$!5qpyhH& z;w~Xgc{=hxXyM^__mMKuz*A4+;cESU9+@Q0Ds{I?^l!E1A=u0P-_r6DHLABVdl;Oo zw#3Euxjt;8xjk%qq&u)2(SBS!^i4GS`QOlgmw2*Gp;4G~ktgMO{^;$}ysct4_s{eB zCCZ1F<#0sYwk=zFPrdZ(%||g*($)Hc(@5m>!?E?{m34JjVQRt|>SNh`E9cA-& zqWB+HGRWOzGwQ;7e{!*zY#Vjxd)ag?s?(a5XY`OG^=2CGT$^~>}P%Fp381so|sM;`~8n0 zbWhe(^14Ubhk1Nj-cboH{!HxWTio@Wvg65K3PX3}oayZ`_U&DisqbM#`r=7d{eJDd z$@X||jIW+9_e)~$t;~?kGLkYBg5q#Pm47(JV=^vi2eq`drMrTRua`_y)yv^_Yydv7(U>lWUY}S;6G#Tb|BAX%SX0a_)NT9+-X4 zvgrxtp0{uxY&XOWx1}AlT076po~n8KdjDmnr>b7|CmNVeJM(lzIcWFvNP`o?aOyBhDQeV12O4{zW#wmPGYwfbxZ9(IQ zyK8w;^2I;a3b=cpPz~jA`trkw`(~0w)*amogXm$8ZqegRBA+iIjNxY=y@uRM`;EX8 zmxS9jBJpRET~5)ywlrSI3WsBH?xpN$g#W9J+s<>}+UCcTuU7J&ho>{^(TbjjsioE% zu}f+>hxFrKVZyE3k>Ua`%i7NO?R}RMAKvUGq%Xy`y>3Q@UYtn(W~Xnvle``4rJ=P* z@qP6lg!O*rGGs%HsF}8~mEIlc(;!VFEv{trlj5O2IPiD8p_=qnCK2Js+HCOlGqE_- zpZpL%OD+F%s>_hCt*t&d`<2j2Kc70ix9XX$UAm{`AvbP+gD>K-S9t@ps&;1MterXf`*5|@0w9RYx-8U{>-O}~+$Fct7-PhV(W9QQw( z;5P}c>1)^1F9~g?5w|7#1|GME*Y_v&hZR~LS17~b%hNyaLv0?~`PYJbhr=kO4}ZLw z_;RSEbbepNVK*eK^sk?)cPLU?YPaFO>CMRU!}7beIdO_^(|5vWYWI?6YcnDjq4#UF zWuguty~^`T*DK>!V~f!i^QF<7oXxQVK@J4GhZ$77cEe{Pht<~=!rYJPEoHE}tS^s# zzU<=$K2=5|8m3`UKb2d?}sOY*G*k zr=GtN{$W_k<9l|?4ML-Du%G^MuXDlKZE`RCbB5PceTEumC=1{CGW||=KH4z5h1G7f zaPaa|BN@(EZ!^`0`W{S5tw6>$Uf43q5PYHpyyF!Hwn>xl*=RL;GM@VeA)Aqaqs#;} z-dwM++*~7A8l88;tZlLm^~RqY`-K(0UyX=k_IdwvVF@#M{HoGQ6yYO2dwOBg+T7b- zQBc!$?iGglC5>metpS_EA@QoO%0pNJ;3nBk7FB`o5A?zv4 z@u4T~-A*Ew8{gf+%6i>{m$(yu;#%mO8d+HHiS-aS@Ar{OGju_D=GRjZPe7|Y7L86y5%Y7Yx&R=Jb6Mumy|+xdbRgJq+= z21Q}f5QsbrEx>w+Zu#PVY|fCy5^Vp_>~X>l2ne{GCJfz`PfVK=(Tc_O_(>b~x0(+< z8~7UL8i-IOi$IouS|I2C_Ohv?z1ndt=O$o|?4_vvkKb*` z_$&fdm<<*_jf_b3W!=>rnC24!4de%Cd9NA=0nhCG_C8ZCc<=co9BYMxhG9U4GCHXm z!@^fG8=|uY;eEHs`IepLjcU47Y6zPBXps&NXhxTOk+CcfOtrNkfB)+_Q+lx%Bgj@iYS+-^Fux6WRKdWE(DeI&Y0K>RdQ)kB>#A ziZ8UTfbFrs9ndEg%%}{WEYxH%E7mu4D6D%!NGsKqWUEer(BL;msyj2x!Mwa5Iy^o? z{QS{I(GdM8r+J0ldwh~<6IM26e%7gP8*fCxPa!0F3Jrs7tuzoyNcN1nyCTMX5Un7+(4+>Y38UKz!BPtRSzZS_N%! z#q7-QD7*QI>cWYq8Ev7{Wq=qIsaqiv%$txq(suP}WRpU=2bWMxEmh2{Ndj29^$I$cia2R z;J#P0pO8B(j0!<-+fyU3R-mg*sPGZR@WS1DcZBvMekBk+!V5WH$*I>IgtB^HcUbDx z?K-4%qbKg$*g23nuuwDrl-4|w_`RZbnbAlrTou!S>aj~56A`EHjGdqKfmf$E zdQ~k0{w0YQsW<~qLj-h>Xyayuu%~5u_w29RWfQQ@MtrCWn0L&v;7u_~B&Ky49jYFA zp;B5B?(Y2;mlHha|I5mB@S`a3`L#gqSFD$LCr(Zepd&dk=!f=Zn?n4K*g7N(+OF>K zIuG^lUR|2g_4ewtNbTin)_4wCb@WnH&MZ1P?@Wdf(u);)_pv6f_own0NqwyGjE_|| zq9+N9eskoHb#cMmM%{lYpJpiNb*ryt8ALr2DE;92r05mblH;$QNB2>(nG#uJ%(#tk zY#3-IkSEV}g9U^E**z8e*7@i`tu{KT^*V0uwuVz>qZu;Y9PiKG`l~|zWwvb69zL;# z0*=!^w7!B_7#X+g>zsYfG6q5J^g#omxlY9MW#n>ayR+?+2Wq`Y|H;Nlp3l@7XG_!4 z>;M}~)7)EoUR6tCs9~1~2N*Ve0q|AH&3w%gW2tgbXMtx*;J`SGT+_2>ccYy)+10j5 zqQw9)f1Lfv{L93m{_2=PrcC#vT+m-)u!^Wyr$gH9a7u3pw69?KGjs1lquof%T^9W|^YAny1pqeH>%ZNvkL&;G&QI@2 z+Ql!D5q@hM`4takXr3&ExJ7X@Prp3fM$~Q;d|s-Sz>|7*brI4+_fPqGsV-ZD)aO$; z51_GEMs6df2$dp-DY<>c3a!#Q7oPo59qeN3bKDF(UoA@DKF1q4YZi_ldlDdpS$* z^*q558D)=H_2CMMZfN6J9%Eq0=}sn&145{TrT`Bt{2+JpW-=O2 z9OyJE=vSm*a)(7imjht>GFtAB7s0k8zbIU~Ol(+I3Mh7dZsIKYSQp#k>k>FyuY9fK^|%b|MhZ6qSlcJGFre63no za37YC5Yhtb!FStvM~02Y{F2ZI!ei1(ygu^50DAjdqXo-C;5<${`gJOCxs0ruD=hd; zwEPVDVciN?^9cMgU{)2GxiJ|3Zynz$nzr3%zQwC<+r6D~uF4`h)uj1tpan2O-xDs3 zkkfQL+tRQaug5XAt@7fVOY2V?8WEes8WRo+RrOsFmK$C&JJFbz>$ z3@z~~pypJ$q6Mw|5QfS`gt#qxBqHozMq}>1MvNY)=$VUQE6l_X@=WnT>V<3~#)}82m0%B^+XbD6FGrmwlysi%p5YHA_2^)b=-VE*>DdmrJ&f`#IkaP*?KjZr?r~ zXeKKymS_5Asjtygao>_3>KgLp9|*YQg~r!%%;e(%jVa*b{-w|~I;c0T?v{#2Z#3<{ zw#(6089e0Fy3gR&ST@(?gCljoy}nv0^}!=AR%^GuT2lZTZtU#RClXs4D4l-oz0Quf zk2sn=P_Dly^}VorC4SNriS>-nO&DzfyeVb?ypl;JhBtp=s!`10IyLuPbv9@RHX?1r zz5o26QFdM$#m`VR7W(+qd`gG}e37H^-7ixLe-i6Sq^sFtE8e8zky^@mzb&271edW) zS`_ot92R*N-Ih)FaMh&w7JKwHLgu10)E7tXZN*J5O~rK+)dPcz;CY z&s)4(aE?pdQ!F8Y_e7iK@1715-HJ|xu^K`y2WZy`b1!}BcH=QW&mIhRM=t3`3bx$F$_K`^iAn49T=QN~tA>UBT<8q9C(rIDD zyLl$nuAN-ea)WV)&nor1%UIFd)UN;8S^syH{f|~#1(YUTGr=cfXidO*WsJskXPoJ% zj-T)|B8krtGP?0Du|ZECJ{F4!{(?&1-Hfn;_;0DT>+G%fMDahxdBUZLOOXk|+PXO{ zlWyp*$bKEgUL5*`0tv6S`7{1Q8jc1SpWS8T4=P6%P;WYNXcovAQ+2X!?(%NWom)E} z?1{>ycNg0fiqKp`xH$ow;gDoEP!- zZwMLi4R;0u_noIHz8febpi=vO2fga{Q!GHBGF4XknE> zuMcHHFEV-*5}c;jr$Fa6Z9b&c11z**;*pP9r2A}gCtK10n3b^WGNqp{9DfWF7}VPS znFFR^O3&eg!E_l<`U-#M*NbvMLU9rJgq`!XmhVuZR?dUt8^6^ZB|NV}1IJM|o(#dy zQacoQ;YGiMtba>PQ-Yyrcg4|{o*nKnm@jlOo8o8=h%UGJ^9y}KE}qN00LVA`VgeCt zRMN8EVoo>s^z-Jx66*a96S0)cX&57Z`BAA~P+jV`9J`it@-lww+Tu7i4JIkp^|XWe zx@geD547tL0_q;5;9UrRAti1MI@IvDSKYz4!1Hl4%50ei$CP*>hpaB=^Bt{W37j4Q zHE;y9;4~!3sW6F~6?x#gJEy|?6BkPb$+JD4pFM5bJUsL)P1horipXHX-pQf$!4-~P^C5g8`H zIXUtx@@t*l;%45|`3*1F`EV@tg<4v11c78~+kK2^G&%iLGv4ExehRyM(U|t>?b8S?m{ja@m5w1a5ar82mSb5= zV2PBco-f^B`*K$C_qV}|tPArrxwH1RpfuQ7?1GuFq^f?kqSzcKo z)q!zV0#?J>k3$O(ruw=5dR=(zuGOW%L#2fQK8rPa&?D(IitU^jpA@f(q^#mz@a`JY zdnoh|v_C{-O;;W3YZfVJx8O$m0&+TsQfi~D821BY2@&rhRWs1s02~#7Az0=S8l!)v z1+sl{x2q@q^fXuLNl#ueGKCfS$3()=%+HU1G?9~)XEA#N-=>J4VZie9<)@x;tW7T3 zzC{1X=#=7;`qr4|-0f$q2WUMwFkLwSir%^6X221afo;@I9(GHOE$a9{=p$r_w|)7{ z#s8ROL@bS~uVFV@BvjqP8s>~Y4<5fR6i~tx0)!hqgN^4gYSDvB1#%UNCT1btoM_uuD zu`8`zKBm4YGK4NHD+IZiTW;Ku;{L+iO3EWk#JjcLd6yT=HW(}yT{TU=jvur}6}P_F zP_}p*iG`f&mtAF|O|yvm8NOJDiZc$D9@III(Ad<>>3+uRzt4hI{gR`yvE{%N84A)f z7)dLlXOl0pt2`%lGQWw#p@g3B2=z+;ML2pq+HVekZeS%UGttjOsVBY~sOJ1{tm%JL z#8fOpCyMPZk9;($oDW5=T!eIx+W|^^qChZ_E82=Wo(vYh1u{o9Rop|TFd4OBB)yp- zas2~#vjF;yYSVlOV}%4hqF-%KB7BO=4;Q(MRz@o)4sntjO?$Y%{cw7joXkg;xeOd3 zB6tZ8+X3vK93=7F22JXJth5X+zuq^V3xxX{e)jiRFzNo@FB?8TA^Vh+UX;}K;<;Ys z=LH43MNbzTKwb|OY~Ld*CniphzhVRpS;Nqwd+C7Ok#!jjKHlo}Vk^nNy;WhDdc?FW?`gK=M);k~l?NkDT! zl}tRRUhFq~S~=srm1$+y!?~gM_k5JNdAZYex@u)@4~=@{6Xn#xc5Fx+hqbO%{3w9` zg<3+&!D3r4(ZRCEKUy7M_*$}nL+q|SH3>9u_u5!3IwZAJ4h{n6Lrdc@@y&pG3^}QX zEmL1;pamsZP=pu+=##^k5kROH6a6SNfiCegVaq*@Hh^gd(ScfxbuwouY9JcgEY{-PJ>N%JTX4rE_v@WP{<-6Qwq7o6DKaen4tOVW z7!5gwMR1s|PdZFO zy!J4|u*LJG7o&Fp`VTzi4NR-ygmW4V;R^>4n9$kZvz!psDg;#i>r>sH$jxVMtB}X& zK_sIn-Lfg(BG*hQxKaEi;?4KtfO|%%$}vnQF)4L4N+fqtk>M+a`_?@ig;W$6rQkDK zM#$N{cOJORhHz`RK`uc1rO)xo7`9r?y(8PE5;&_OB*fkI!ib?O+_rA^7 z=OHtI=0y_-N4={N8%-D6iEoZ+WFAO29%(c`f2Q*j(E&cHK_XJ5{d^ygyQf0FASfY5 zfeLmHo5lH3UjoL6+rAvYZ4n8|#_pHSe^_Q~FVmGj9v5)XcsMDNw%wT6j$BppZ- zq^=m{H;b?Mo9}vJ!p!{R`#w9rJ4IGX2>9#8bI8~itj}_-w7sQs=oC`->(cm>WP})U zOO?ec+9!)yM)(Lh7%r+wiwsU$?zFI*fcBhVzY=3y=300?;W?c{S$2K7_*5nyh}lAi zPUowE*%0qumtojO!d9Z zz(gE`0>nTzmJg~r(xYf#F73f^ON3y6IMG2joz0S}Rz=#I1(o@lo&cEnC4_#Lk<}2Q zxms7>-@VG&zf$F)oShMZ$YNYk?*k$5rnV2qPwenf7yDbsJJHX&%-j=jJxAIM@jO)S z@z9*riwernDP_ca5$|@x&Qol4bvyGpA-^eM{5)vPKSWubLR6D6l^dm83S& zbokpy7F7t-=xc$LH6s7(7paP0)^fsZlm2vcbcg_8P0pKpK3md+-1m&`4QQ_z86n4o?{4t7ML}@7gYWc6Mbs5jV=AQ`=kIzj7cdshX~Pp<2^r{#9vNGQAPuedQ_{^9ZL&yeTg z?oB!p(uXaVbdfZow*SjSphL*@*U2pn%?3xaiBBlp{4+V_K5CN9hI?VH z;DapzE?pcLHh?*RPTYTYeY%LQeRW#>8Ate>OF0NM2|^T*@w3htnn55td-G*wXu525 zEV2O@7*(NAHgt|8VkD1<4PG@ilo6PI@OK_`O*!l@V%ig-@#}o3+>X;F z>v^0+mf?avfLTJg{WgJJ2^GuA4FC9ah4d*1K{UgHl6A1h@Az3{%;O;17et-nzsa+r zqN%kyJ`=%EF{r?uG>Uc5c{*bP^+fJKM&n1pU_v>EbTc%{nnxY{nZwO%5S!~6d@PQ` zjr$p&dnlfIxFgh@+@@2^U3Jo55ZmvD4%>?aVtu2}WJ-rgoreEjHXZjVo6OG_YE0F= z$`DqQuevp!WHpwH-$rPeXyF%-x`rlJrmvrkj`2Yf*zJb6r!XqSh|xZV=?mn@AiB)- zq~!-C2-={7y(v*ZG!4RjzFxyrKO)nUisXDfo6PPKqktc7O}_(DH<=hoUU9~ms;gM< zar{|TDl_C?Q|tHm;C+DFRet%WNM{6)>!N$J7kEZwtxf|l_`{!6=Y8~SR}iu|p!vWb z4{tk)(Qc~U7onsNUO6K7ayd@MKV7Nbo=}!;r_8pVF3GJl8(?2yhNK0gF3^gwz;E?v z6#{Fhy{I`d_@{^#ti8+8R&vMyfzh_x)~hwVxjHv`3r(!-sf_l(g@;z)y8y6Y_BK%i zu?RORfB5LrOpY4cAcrj*fvcungGUDndv-zLqZWE5-BBCoDdbeQ0u4hQ`-8v8sBU@2 z4ApeJF#?yV<$$x!CN|{!0mf;w4Jnm!0knxMK*a5|?oGZQO$ZSaE;&0}mQSMzeG-Qb zw~k@erPozCbC$N00%qoezFPH@Jx{Q2%gg5fXxPO0fn#`5ZSYI&a&lZWXr^wdfMzC? z(o8x2M{HO0P`fDb2~B|^2U@GyQTT~JS5+B@%IgR82k46em?FU}F9uUx`SX&~Nh6ni zrj52R7D!SCW1dEtxy}@Zftn<<9$w-3N+}-d4DJe*)x|)&&{yQh&%g*d+LhxHc4R=%pnoG_Y4ER7|y9ch>kE~e`bHM!7vnIW<7~HZ2Y?OnM(#zq7j8zWG`t=#?`|O`9l>~> zlq6YaE<+d7yVn?+Bs(>fzKQ%1(Q5mlhcqX=HpA$N^zUJq?>r@N9#ixMqV`aIO9Rem zKxf_pidY^P%tz4SLRD`X!``?+U8rYjQ&_%`BVA2BHFKk>l;s*@Wweq&|V_j@s)v8<=yuKv|Z>c;K449Fkzh&*^SFf0IK9Pn^<%Q%a62)w34 zsK0T=N_hOcxEA-)g{cLH+m8&ZUNu!f%7waRi3|(8xr0Nmt3%9 z0S-S%7bk@)d;3?3qxl<0@@ApzEKOXZPsHd(lS}Ej9|{82j6|z! zcijs8e}*#(f@b%om3OMF(Xt>YW(rE}qQ8>d7bFjN+!(;&X_N9e39_eL` zx?+h3w8C55tA9?XTRN{04%FhMp}Th*A>{ln`c?HryaW4<)%JT=G2mD(v#mZ&bU9b; zhA%+L#G>}2GIa>K1^}P}LW$A8Wf;qc;MfI~9yZ@bt=hjAcGxRKhv=Y)xIn5N7gFf?yg33LociWf7 zoztlH6Al)L=;kRKS^@TEatSQhu|))fO5n>6ny$t5Hu}SS)83W>guW+o)?$zk zgrSrM@1O}|?VQjIoT&xIx?cS{-CaP&pxRwS#y|Lq%^0{;gGG;w7v^ees*@!y;mH z3!`9XSKmv2M#7b$ZT`H81IeaEbUck07^nWWdcf{sdmz~8Ipo^t@HN9_NPF|;3xi1Y zAEyUhZs)-RQbW^%9PnEnyXsFRo`N9#sXP_-A4|m&&2FGRqc?$A1aYDcsO{1LurO1q zclTU45xZG?Z#-N$I{@QGIIhj5-1w1&0)fw|VaIFE?1t|2U9!hk9Tp5-ys3T1@@%q{ z2ILmRd!%6UwyOhyd`=mlkc3(Jk$#2MwD^OZ^ASmhhV8GvJK!xZ)dJYl7^BvQ#H8Xy zr?o~7HmA9+xDBJ9Mc5ebhq(OGJ4&v+w<&`dP=8ne8F zp9A%0i7-Z71c@H7v@-(pGl#N^(k?)Ae`|2!S=jM=2^`Y|&b}uH?q2ae8PP__iGEea zlG+}W2~u<_TzB9AQFr8&3Zp|Tm&tQg{DU1u0X8%?HU+FQXFJ_4E+~=Mmk<2X$@vNg z6gm*=#-Cad+waW3D9q7ChSy_~4{Z1s;vpm$fq$yTD=F$^5XTOMGEPXFM5CA4J7{JK z`3r|$U-e+XFxKp5C!1!AJwy>xcyq9VUb6-RMTr5E^R?Q(t1bqY?-2Gn8Gl1@*T1Wf zeY++#fHhb{_Ge@8ZMd;y7HI89ic@5E(-TKvnb}P`t1B|%`<-{X5kuXTFtg*!e}03q zbc=91G~2skJsi|)*)T?)=wi9}Uw^g3g#ATP;q&ObE&9B|j|DQN>ukx(?V46Bkgv@q zAvd>8uKmTfsIS%oehP6Wejv;`B;Q}Bmu2^l>is`=mh1(fA3$X@%Kx5MWuleK_zxjV zo0mKK-ea0G`SmAFSj1(KJ{M>O-jfDpfcFe5S8{XPT7M@D60iu%*h76QK;Pt=jMJWS zhE#rPb=JFJw}ro_k@Bu5)h={o`TZe3iga^0FmXZbbafzIyAji;fkQ0{bcAaB!X1e% ztYe0hUdH?ao4x{1b?g--A%Yl`&-zdH>z3I8I?-WdWT5kGHMNY6^;9wMJ1eShojbgy zz4o>0g9;xpX1)d$8jxTjw*2pRC=g7lZ5;wB$O70^LdnOS{<|G|zR#hVLkH|ITQr#; znCsBRIhX%#8Rw&eXs%LGYkdVfMX?6KvTcDv;Nsz&)_H>lDcl|ENNirdkBeN|U9&Zu zn7G*?auz=)!Uh#>75hv^+|#G?-=Pcd z4kKals}GdB8oqc_^huU@PzkE$+(a(Ww_=#H*oT#PaW#p61HzOB#kNN(LL^%svNWJX zDPbQuStgDqb4)?-1&x2mywZN~rF^o~*N-Wn6XbYuFq-BEHZOLarwH=-sCv6>jtVeX znL03DE%CBwgCDkjCg6Hsl+^H!Rsg)I#1F}9;xq2irGOaYo_2etB(>Q`Y1$6ye3yV; zjQ3Kkjha{4$EbZm``C{X1vOLx)`Hb{FE+WEUhvI##ov3pXuMJ<9 zHa0Hu^X_K9ao!yD&t(XeL7TH{t{e2zgiGObY*`}sc&mF`Tsua!)(2yDa#LmU#lQ75 z6k0vgJs=7KCsY*_b1GR7_Nxb#_>H}SVOFT_AWTW@Tm|{R`ra?unJaO1{?h7~hYr?o zKqO1PHy!8Z2TQ+5mT$9w&c-RN{Zgdv06v#xB{_g^S4+UZ{}6~WZ~7(4Qf}^u|Izv< zBifSG8d^s!JVA5f0fy4MYFz!Q%I*xD{h(M@RYka!Lmlv zg&&IH7jhzS(iSrKRt(w?h`je%fgfDh1xkWY{!Y$a`9~qOhB(jH#XgSh4bKwTj(I}-nv&tK1#NSJW{<1CjCCC0 z^BBKaJohrsFNWr$S#wE+V&_2HfDdDpfF^c}BH6L48u4Duh)wz!O3Yr6wS-+WL5=mY zJaWgxjUEHiAZ!gqoz|PfcV{FveGfm>?-#)`)r6|8&d0H4iRU#^KW;mrOJ0M7x0I{R z;>RiunYC)Cu0y0Pggp=MCO3w(z-OzSLbO-KiL8FQq04UNV&MUV{^Rz^mJxbU2&`@S!;Sy6NY{^XE4=|v2j0Yd^8lr;~zKxnI6N-~wde^Z%)& zx%(r%Tl6V!-SvNMHT-w<3<(KDRyy7^kC=z3>XZhxRfm6>IB7{5QCAow=h{Xl@qar~ zCxGmBqJVovYi+Mye2U-qr#ekY!kgkAQe^W$K%#2y+xXZ(n}!%Qi!m{elbn*~G*T2_ z^UIld2qkBi{Po21;Z9VTl`GMx$@Q!4xH4fNH5gNDS0{ab44!fPM)(cva9xK)KYgrSa&5W0TF#|2970es6oGFgyL70J+=lCKt$>@ql+~ zu7Cl>5(G3{taiquFuIq&k*Tb8I>lXYH^1yDrS^OoXZI4irPtd+rS1Y3?Mox*U-Azn z6M6K9^~TOUQ1VLZfwtQ-DWhihu}ZJ@IeM(yC4EFXYxq|7`2B|l^HyeIi68N+PeyP|_E&O|>gI()4 zTVA|kedy5IM1kc5$=T!rt zpg!;>y-UOGmRn+YG&+2@-Qb z+bdemNMw*iM|Q_mM@m_=7Es}2p}9Gdl>s^xS|Fr|TvM~vBOV@5r5yRCjYrnyAa))HI z|3ztHJE&GUkh?vhB!LeP;tNlEg7GK{TubIO%M2y0=86m3_ud6@oI9bpuTqkezdG@M z28dqCwSH=FY$k$qZ*pbFrYBQvce5x%y^l==69{R=a;hovBB0-TDaNQq>7+D`@~+>% zI6*j@phis`bLU_{v&v;x`?q>#05;#J_LP&e!W*;X;Q%(iZBH5ZJ&_8_!2~X*u>IZr z`JHG^ZF9rvR;M9-I76q)@Sf;f$~1n+ z+u$sy_l}8qEQL+#Nju@m3d2m|ZCEpuAa+)IsHc_?*1Z~QFosruDxUayNAChKcJQ#z z#;klR=KaLC&pgrk@D-2qQpLPTlrcKU-T$Z~3A0IO=jI;9M0 zIq?k>fDTQIMPmgM(N#-I-@MPS^9bOLrW3J8jB@UM^^V6kroz|Lc#YB`;IC$q!bAlr z_xV8qo%mLoB>%HVh0YmR^dnQ0?h#DCx5P&GBPgat>|UsvNc^a?3K>Q>t2ubp<4aDz z&Z%3D_~X3i(Hlke4!VSH%7vI{EvC-AYf?Lme(>td#oxr9dVug9v@DE3n^N6ayV_4P ztN$x}u_%=G!pPCbb#aCtQQdN<{}uH=n5@y)nPAc`(<=#@F4p>aTQM>i&yk4tBYL@z z?TOiLz%ocoJd03u@>BubCft^JH@hrN*-3>A!&TbD`sDtjV$+{j^-Z^&u_i;&Z8Qau z(n_I@;kER@A>%zQ7V=3f!@+KcWqpDNKP}hnvxm&9u~Ro}m{2*%wj;cPrr#&%rRlL1 zZk}zTEgp}>zlz;6*9IC-l7+G==7d@(mxRZ4E2AH$`Hcg;Dc+g(f%LU}e}FIG{Bo{L z!E;sAV>^wprfxHw0P_u6eiET`Rk!(`=6muw)v9Jz<(rjS;^Y`}Q#M9WuB&)-q@T1=!=m=wJJsFoHiJ#Fst`qd216ZN9uu74^`$dfcE(6bQH&)fL6 zd8WtX*zzfzkp(5F&}NPNawy#?rmfds_$K}Ga?we?#dB(Q`B3~u*zHo#od<0a%)OFd7-=leN(MZ4h$WES3s?MucQ&~mz5v_ zGuotv!^ICqn+uID-#?;eRnEd`<7oQeHurO}s)S63^&SC~3;~m-3HpFuw<5RroUi8$ zrI{^^zw7XNOgje81sy-Z_9Gz!EP*)& z>jVy(5IGL*;-fF-G$BWwAw+H)fy^kAt?=bz1vP>=4x^~G-snSgSyN7Ylp5&KW9PU) zBRejd1ZX}FPMR=~1lYF`nUjSdng-W|2*JHyvXS-Jquer7H~v}k0q8VTTOLn$i@8b0 ziu4St#LmQ+C+Np&hHd9JqBRug_x1068~v1Z6N=C_{=DGTBo-RJoz>Cw zQ++<)qse@G9kqqU(<0U6+pH7Y9vnhMpNBgAN0+LeRMykQE|{xs98xe8v6O+lz`Ky+ zw_oi^*P4>M(?0613@VszXq<)(mnSOlhq3)zDyjep75;b^CFDXcMQy9dL~WYQ2O{aC zGTTHb9GmoOgi|A=s6Jf@^9Oq#A-ULx5CM%_FJcBah#p5@fhi6|T>m`%bsL89?4p5K+Creo0!n(W_5OLdb)S`X2;908I+_vATtkXli^hz)MEL%9Ad5n#`R*T%-Ma+udXJLRpZBYn{1^;h0(m(M% zC^PJZ$4fg!s2ITocKMwiT8KTHz{BmaX4NZ|-@2JhD!&B6kTL@PmZ>f8jcvdF?Tbmk zr*t2~yTAwjF0r4G6Hr26h|#T34G<8C5qr;JS((%G)(2l!Pta;o-7!!QvBy9g2Hm8H z(5)1(o>E2uFcHZzIFw2*-+s#c!Gx)1zlw}ob2!2vf6d=s#Z;9PAb6bWw#57H8K1Ej zZ0BIH=`H=}Xb90==P%1Y+n<$wQf1*%ytDzzF77LNwl@d=n(gHBuP?i{zRTgUOn&reUhf}G}r;8{yKeBk)=Ze<9 z#ucShn#}Y851$#;D(kWTXaoFHPy1q+JwI^WquRDtJuP*6&H`cB=WeX#w_yd%YItW4 zgVV-^ZGl%P^RU(lHhVY|W zkLEm(sRa8GJj$^36qTv>XomeQ#zGkuJcN99ZMdUTW_oj)Dc_xtBU=ud`rj&e&hB-J#jjBY+*v?M2yNAFz9+dcYGG0-7%i!M|0 zj|Yj~+hMWWN_!KuT%Jd3{;N{dT>nXb#u=4vx#9p&=~WPlt^MCx$0J1$p;IQ$$L}T{ zHz)y{)nu`3`sH;>6uq?VtS4NbM!=rf2PM$Dx%wF`3UuJcebIXZW3u{Fs;Icc-jPJ9 zQ}*#IP6tq6HSqVPfawkXaL-hcPFaS3Gy0@U8~JyNbuk!bo*ynbC*`WAy$Ikm|L~ON z2!^AIjfnY9!Id{`AoiVMTFZhff?5i83sJ(vx7YQLr<+3`I-UvG5WmMadAt5_z7@g&Pdsbr z-}CLz)VXo);kh2#v8TY|_whK{q<{ zU?TU#tdoW1+@z)R#sFDvv40@e`zV^tywJFyDAtc(GNxa*i1f(`y?)%FFdDDS9o$NF zTh1)jt9(D_Y$-ZV4?98ie%S`%ZZCc8u3S4|umC?V69LIoCPmy_TRTbfYY>j(WvvgB z55qqVzgpfBXN~d&{u6x@2v*ad(FxAwhyR?h+&< z1Pu@z8g~oslHiu$65I*y?iOe~xCeK4`!@5;%$YfJ=6mOP|Gn3B{&3N~d+)uhYSpT` z>#pZa4e;>p`y6Bgua@4DX#XOixz*!F^eayrjAJIVnr#a12`87rqmz4?qNhWTCo1l9 zaip3dhOqOf;*EL=Arw|?y{)mDdjn+PXE1BljO@?VN=Dy*!svMbR~$6!;^TMte>rKy ztK^%uOj{3k8$3a>Pb2IDoPcg$$BhS*Vk-=Pifs<3IU{`5#=f;tH=GpvVsH@UA0HhX zYkwfg2V&E0a0q5MhHgm%BsCnfjV7f7SiJ)fT=i|+uR=F^4MPqzJW|F714nPf+`?hd z7k;ur(T|p*X4+*67Cz(XIjP5aB!;_t;9y}J7Bp>}u9s9&2VJB#v@gOG(0-0&<0y(u zyWf^p`p6y}CK{u}ugkAdek#fLXn?S1DB7XXU0HY1R=#`X*o_q+5jUaypqGxLtsn)R zdf~P9Rph<&#X)bTe)9)35m5Z_xuBV1naaDZRynWLj(rQufHd}BVu!dpULwv2-3*-fB%8$J(zr*bYtnMUtB0?b^jf`9;90|&g>a)XvPG&4F>nSNZk zvzm$28e!2(|LCrFdB=r)tb4;+@HkU3@*rIH6Z+KyfBR(fJU}k zn)gj2yb%JM{G@e&VRJTC0Uz@QFdB<;Mv6;rcgP zaKW6p6eK}(@tD|My6D8uUn-doAL6SXVjyKx>)zm_NH~q}j#-&#t?;qGqyZJrzf0s% zv3>6I&7@>8?>k9HyZT&2KILp5?KjC8unkk2Kl0p9nHK}g9f43NTYoIVMojU`*YgPh z`LqP~iq^qwqhG0lU_f*&Lu#ln4mc+gu#k|lAT5Jr(5pe>{=G?Gq25F9LecL4o51uS zMbvhi7)j{%_VD8uIHB>Ep;Bo)tRzwU=Tcc9__6g@2I(@UC(x~J=$)|vLt!LZrPmEE z;geKo9l!M1b7_8!=*kB+pqP~r^}|lq3N>E# zy@uz0pQ87AIL{D5p^X)U<*2>o`Sspvw(48+ypTZ;I*sK;(M(9I^7e;kO$G1)2}tSK z_lvp15RY;zCy`-Y1>FOrLW`%wm}2$DfGAp7^F~x?HzfPkt!=92lour{5^>RG6+`1; z^%H|mQ{BE?j$BI8n}}yWPp?1gO%&;Kq<0J^^9)AKVm7*VCM*?E%po9x_0l&KD<2jc z3fN;^#+k`qMl=~ax+obtyi4Ym)eu(&4lxGEp)4>!ObEKu=ya~(plA}a7dRt?a^Xd| zxBlJAV;yz2(S~(t4*%7MF6A@`uO)ydw#pUtegO9EvsaFL)887Lw#`9=`r6$|VUZuo zCRwy=Vw?_VWa;EmsS-4irlFcBXAfZfgXUWPVDoM9{e|Ee4NCG0Gv8TnNyDazd>!$h z4}04j0Ej4!5&aav->sm30tchFRkQG;tu$2c#=LBJw{}9%uT-QKk;pIk&g-HLzXbiN z9G0jj>^P|-;Zh4$B9UwRAzB0(>R*v<562?ty^2HLXgd|#8 zaVSKFcBQ#4(*)@fw$I|XK-e+vm&cUCM2|v=r00LpDzkyhzNRAJ@6pg#eu__At6FFr~N5(zmDe|Ijzod;L5-_9NL(Z%{A30 zO097@X8NT&Ra-w0t2UCIuhM!ubUWa0ZbBj{tVQnbX?}efH~Po$3snr~vJaa0wl#b? z^VM(CHtK{`LE9rxI4M^MNNdChn!xtubAm$be7(JW`by3I9CDbGF_Jo@Z@R(JBH!ux z*1mKsf9wZ3%$H(`6M1@_0fyPe@2cy@p=PCbVctx7qyV-qWJX z>%3TzQ6O=^2i%hB@-%lA_Q}?n1I%4IF_Zb$@ z_pywsx!?KhKM|spF=Y;}55(tjTg{Ec6ew-=5{R7Gk)rZD&0>3CfV7VFhOx|sQ-zvN z41Y%1DVc*t`zrao!JD!M4vtllvGx*oz|G9&}m z^H|%$(Wo1hY;^59KFo5M#ATsn&1*hcSa9V}r&^-*(i_=*tq+H&?k3IqVI>YhLPHBMa#`HT zoqagJO0cWC-=|*R4ylrae(3L737aOe5&lgZG?)Y*S8qdfz){Acp6CPrWBdyxRznEG zcOe3TzKehXn|0A-=?xW;?3qreEGpm_q7Z@+(9tE>_M9FWhvr=_JPkOQ;MBJs}o z({`d5(m}abzf6E)g_1RCiW-^oThecK&Qpfaq=}W>ccc6a3}K}b#>*W@qi|ZtM;627 zWGK5iOg8H>vP+Hy5(9|b*}hf-AIAG#^R;}@V}v|ENTU!IadB5#muJvT-r0_e&7=r1}wnjrKe?ZWA%N~7jmzrGd zyKOdn%@oPQoDS~zDzg=7iyV_Dr8OT<(;i3suF+hk0Wj&zz>zH;9Pb*4Lo7d(L_D6P z4m*s)^>jm}0WZuB!~U!6l7)0{B;}@Kryv*ccCKvNn_ZrwJf%E&oSumxZ2_~%J@sFu zx*~-d)rDhny{DhdK*M^qB@4*8mCt$Ww64dcNkzN_Tm96pI=a!GUA3AXQv6N56W#Nk^3XFGWSb>%h5kDHgv7B^*cUfjRDF~tf z>upy6v;=MnZ?7HkdO?FIkRc$F7h6LM>2v-10=^B2H@^1bWuK=FmtD4@5s z*Yz0CD77znKgyEvSSm+>iqz`>9&Q;ID6}uxw?^_N z-%B>Sr%#J`;nL^k3Uv+e_;9BD+@2maaJX;4IU=3t{h(~-tvo&a`$Y{djWj)jkn8W4 zOKA+gI&OOp2c1oP?vs`|XPJ_rWBH?5^w#sX9~o#~!wWrG-2U#Auekz%x+HGSxZ0Oy zzSG{44xpduqA~tU9#?RTuQtHX2^c?e-Y1B6?NUh|3y2~zQd@1a=kV<|_ibvy<@xH@ zcU*?bdp(8@X>oZYDc34<=iq$aJjeymw7-jFo{+wpB-3T9wHB_#xE<)9VyP3gTzi;i zFgrw=qM1kY?LoEe9O?(|J0uHy&DIk-9%wA1c8Izus z53t$j3OTi~kcuG&RKhVqYneEAO(|u0o7_VMGTm3i;}(V4JBbsemKY7>-Lfyz8Xvw! zd~eM{&$wjR%C*DoDlRBmAogy7aU%-i{do6Gx2FLql^6(f)l6w`?j%d?zo$DKW zu$iWtKr{B56|irp!(aqH6l-wNkYKL0l3l0Z62=Nb1WYH362}am`9Mk!1S!4W4GJq_(!*^H zjjrRyG|&hn`v_~67MVo$xWG{oc0(|}`~D7>xS9hBs1E!F{k#p(m&E`N7ENix)!ntd z+s#>lsw*|49bk}DtoJi$BiLHZPuoc+tfG`4W_EaeG+7P7ql?g0v)sQs=sbN~_Cw+ChB?!XI;If@-K$FUqE5eFauG*RS^4ar#?#`0^}N59 zJ(%hDF^l-a)e7L>$6gfJvMl5(L~_!>-M1@|01~*U!3-!f?)`oILA(i6LrL6stFk_a zw8erFRsqGimq^9grN(>dl~WWa`Vc2))4&tuQU_itoVJRgrQ)m|%16<2VO<$Glf&$+ zM?Y5mrV9qo$s+jblH**{fMUiIf&p^O2nkxB7kU97j3pR56Hpdl_(vXbUB?=BzL;r% z#DRqBUDVH6@Z&I0HutEpYh_xAgfFS}2MKwWuS1a%J|LMEJ+5c!!U*wqzJSWIE`I9S z6<%z5jBy~84lCv1niMHT!}LYJR;L81#Tw0Aa2OgNsLJ7~EW9JxQbxMgF)m8|Xdlt& z_297_1ydAKkG`xUrDIA}7*%$GBZ!;S<>`{z0i&NVaT-_^ky&3`RbAF>YRv25xew%~IQs60M zDqDJFA9Dgm73HK)d(&*Q%@(x)*su}+=NWmuaVr~BOFTSR)&Z}=u|-}SE~Swl6(HF! z`f=+yS^fSJ9Kry~hBK&)P4Go)Pc>6*!@pYzq(sEewyI@z8iV<+08mQPygK8y*kGc8 zrXbn7Z_1?*D6}-5eIqB@t1bnEAfUX-H-JKd|g(i*v`UrtpXU6C0AszR&Hff&N@9UhEz0zx~6bqau z5976Ok%G1y6JtM@0km19qH-e^`3X%3(FQX-DxmCE7$L|uY=TUjO5^Zo<>uqJ&w>p^ zR(!ILTUzCt*yD+GOQ+5Rwc0~JSnNx6ly5P| zGxp~TLP_7)%RK|Gw)9E)v3I;{GaK?m$(6kLWz;&K+5IE_=oyAY0OB)SJwsEWAV&&8 z+ppFO4MvSU;bc4vNPXbsBo0-%xofy@M*yv{!%6l%_YV{TC2GlSr(QpYp~%&iMD0| z5foX>_O0J7CCS)sr%ugGRvfFQ9OPFFLXkhaZu>vtrm-QqlO5V(d~|8eBNJr zHa*pPx#)iOvp21d{1Fm11{0uYR9Z|=-fjf)Qv5hb@( zqI<2>{JcgARQ>z_M+Y#>Qz}d{)V(1D1M9V)mW6zoIE?ctM+sPUz5`A=dODOKO-k?l zHNd`wDPa7C77HVXhVmZt@reo*~P~(^S6?q%`-JFj0tQu@rzcKR7Y9%D<)w}?gGwtHLKIk11 zwkFl5l+3&8=OL#eZe-DMj7S9HSeQPN5P;1Zu|vP`Z{hu%JZKgbIk=jj4kW$9f(sg& zvHi}^rZgRxSmNN@GF$1+ibnjb^?O4&g~+WEW{pD@Gsvhv3OFGb>Tp&*!_QebA28$r zDe{mvbtTNn+2Z|n5VxWAW^=Fg;Dlj7*7Rm&sVE zcHD+GZM&dbtfb0Nw}^$emJsxp8_#f6|H<_s0d^5P>>i5mw_JNe&GCv9qD!k-ziosT_7!m;WIozxaEGg63WBQ^Ty=D|HxvD7^W%&XkOwOc zeh=@HC;)S$4>;JA8A1pnRG0J z2MhcamX$g{G(^8>Yc)~^!%=FWC2jW=GkxXT3#qzoCczg8lwi3hj0U-Y!`gB;&uQ~J z*<|jV>(h;Kn^xD$sId`ZIx1;X(CWa>(a~bF`!gA=2X0krSfS#fVV=#7B_L-So8$czJ^&hdZ8#X$q8qd?N;g&;y!jUov++i)&u3P@oQ;E4!$Ni0vs zZ3+Go>O(Hzbi}tak|9n_->-EXw!O=*<_ePQU7VIFLxROq+}Egn2Nv){U@LqJ7{&lu z9dQr)q>K-p14m>9ruXB$GiF?gW**D_e0enJ25=vwf0wX>Q_J^;!YKq|;&(q#o@S$8 zDWJNc2m-+wv8gLtSo;s%s02@rMeFKNPx1?)Joz-IoK8d5swdY-iR@3l^ zhy#ql_1AWB^pEg&6A=pZNMRoAFIKRU<;QGVAB|=?!C$zFzXt|57KsiZHOrE3~Z#s+r6mig%?o8K?LK& zo;I?tQW4*bAj+S(p-*(N%P>AZY%Y~pcAsWHPtdDjWG-A^^^xYgmqhf~1AAZ;PrH7^ zhW|u@8V3s+vuHbGMFV}!ir>rAJH50;6ylR} zN^A0nbPHP)9ap7XH_nHFWrs6SX8Aq}K_uG96$n#%4;le7!X;mLO%KfZaig?*?k$!D z`EqG>Np&-V8k!eh6>^e8J(Nh?SCG)peh*2odsV_HU;X?t_{;sbF@Kk6D%pi@b~$=J z5%RlaLEnsNzTHT&HZ4%HoQB!p%gK_DFd<|D0UsuTpw^ZFBBr}O+ac940QfHGO$dko z2qcer%|;2?_~Jrc`O9!=7cy+rL8;xIcpHoBb$y?t0GsH3pE`i;n32P7DHY}ijBs_- zruTh3oSlGoZ3O0nTj=DTkUXBM#Df5t&$eSCz_+B?22S0iL7!eq87z)W(`fxMHQ-5d ze+7X7IjPr>*GmKECMN-%uM`%y&Y>5IyBR-_Hzvwy&r@m!_mk+RR_n+C8t8u@BWOU> z8Hao3eKrD`M)iNCTqZ4(%pXq3tktD1)~`x04~~v!P+@>2>jWaI$Qx+qrFqBeS7kQJ z_J!@gFlR9k!N)Wrm@fUX%u+7Ls3V4IJz(iHVIl98VBx!pLn3R94~U$gGY&VXDct)s zksBCpf-)EwbZU4Ridcq^nKu|aNht$xo992c}%qLD;S1k zDuW1kt0a*{83E@%q0XFY6%l$6?^e$#6+9KEk5&5yGPzGXhkY*e>}XWH;nOv$c9X+5 zNRqlY4wesT0tbgt|H}~68!jL){7m5orAGR5fV30U&0XQSj1UY}h)Fh3{0JTb3(fk? zDz1h&gdfZXZ`%PNV$m4adezRlG=dzy0^~&Y0o7SjkuAtqmyh-K8)JY6fNu+a-Dvn* zib%3*sn)uIJ79k0%J|)S&&a#zK(oy!3OT*oJuuDVHeoBPjP-~`08W}^ZvYID&1b)P zbUnjVj7KQM2}*}HIPPBAFQY+VG(fAk4=t3JLTRlG%K6LA>khk<;U7yiWaC+NonpgX zaoUz}Uc6CA6?COnJ<$3hEKO|wpA0{+nN zSC2&L2TA#v!gz^RW8~BZ>zQikCgivi14A4x4t>x)udQx{5uaas7frx10ph5!8jowc zLPIPEXabM;%Ua+Z=s&Nu=fWMsQ%Bw6Ct8;UU_9ge`q^&X6Lw>+MfYNT7^8<36!g~p zzQ?g8u!}+pdD__fan(7A-8BRm_1XeFC7cjL&TUET2;`yif!iqVQfLp4gkDbbA0t7C zF1F)Ybbmu`r6QFPKf~Q(9W}=cDbV9bm?*aJAoqHu>)g9}=my*d)}y-DA)FG8#1(4- zxi>HD{peX6bUs(7b>Ff2>?hD2DSlX?_P>^rnpRz@B~fd#9hOVuAM{TC*(SEtx)~^h za;Xw0`gp*oQh|?D8Ny#~B0lHtJ(~;~B52hulV7N}r$e&0S!zk4>z!uO$>d%75qy_g z(mqd#xVz}SW3L>6wb6(d<_Y>?L?YzIiB#UAa>ndOVpFz>%3TjcweUdJ4Nt)t;I!Cq z&~$8&)|R$;eTp&D)~^kmT*d3uK^IowazTqpCUMS1^^6F}TYz7W?=1?_jTmUU^kX%t zO7X{7o$FRrWb`ThVIm$Kt)0c@#u4E$lGES+5O5WjNYG?lodFSkTW-<|tQ0WfS}~;dWx?Hp7H2!BDfK2vsk)#Acx*N8tuAvzTFo{f8_*Zmi&=5$SkBZj3a7>bBsdHh zrqet_VIrS;xuudUbeorJAZU?Uv_gjq8{gLI{JB()aBd|b{t<(km7gcp4#o`9 z1~^l5kU%P2WOY&OZ1<}akfJVQpCnDJv{6~^k8(N<^0kSYL_QS|9TJ0)v?yk6T+Cmw zxiXk#7MFwO46&@_$sCW7kr+OXRCfB!Zw7y?adAG!frE3(*ZvCUvDVkQX+17*ACH%c z=2Hn~0Sly=tWp@@%VfHE8MYI{!7)G^kv>#Zwuo<5fPSeL*b?U$46l)M=hl+9^rxKz zq|4)b!@CN1yrly_Uks81z)Z4tzelBHLq?}6oywNRR|;YKO6UxZ2f7kK_ICo5-3D#H z$ShO4v{2$SJWF`(wkeRBVDU-SVZr7}Ysanvl4PJa`y8%})kUe1k^4`GZe!-%$I*q} z*6|2~DN_tG9=~sR!*5Z0Kw+*EI9Z5e><~gj}Q{b)4g`h}qdn5ku~OO~cW3{iDA$NRHID&3|;;TG|r^1Pc7cTZRXFnhZXE z1@-tXSrE&|*aRZuGtWMWt)Zt=Q$2gGI;3?6(QdlyY6q)z!ABG8boC?ky3I8>?Mq9B z7V1jun0cIwOF_iO)j!)pu)ujNE@o)73<;MQ1lK#erB^U}00J_W7G?zHE78LqvJW$8 zEBG^?fUC#57nrI?9hyW5CN=yCljA#HJ_a9)v|ckW+GK#tzG4Q-UR4@U<1E)d)g@r~ zSeUPh3|F(5LoF<}(K3%y};OOLCB~0BxQEX3q*1 zns@pWP2>UDG>n}ZHKxq`L@2M|5-)r$ZW)-I&qE^deLH%vtq+sKQGp5{GE&eFujZR7 zARtz2EVmH5(&gWRSY}Ml>msU$hd51nO4NqwBD{w>Jq^N!TYXzRTfJvo8H;&3b3z%f zlsswm)}$1!4Bp!*bt#JG{TNVCfVXY>WB2nIdhAnr(Ua0{m+%{pNh!V0?alDZl-{ZE z_VWPnS|+g-dZKUXIdl1bsOZP=J4j?_T5OfoRNSFs049mK`(hOXW5S9*DnaIR@xYP1 zW)YP(8&kLhs$-Ly62Kp~%jt1t`|vG@1VokKc~nyZDe*KV+xQOD(Bbj?wfn+|X|>iL zBs$CkW6*cc00g>a)7TdFANI2%MSlBOmbs&}^Kx@cVL+wHNfb$;Ri?SMbZnKlU)wci`O56tr zm>h=>F6UoTlMc6m(<|$Na91Y!E;}hayF9(&T1~d}I_b+t9D(+)n~}_4O|5Z3f1^ zc-<(qbzND-OaC(IC*-$dyA!wj1%p`MoM(L`;_N7PnAWNr(!U{ZlY|Z`S6pBiH;Jwt zc@Z3r7WM`5wS#i{LJ$SY3kM?8&e80oMh_#5ZnLIEgM#3gfj_lvd0&>kWBh$DT z+hr?u?SLw>C5dTH?4Sg}UI=>WV>hpp-UZnP)SU|jdL7E@8H&340|s`QbkbMpBJ4#mdk{g9tG$t%v|a5jNwmes z0Qxi-C~IE66Ng99%?0`@A_`q-aC+fGN(BI2v-bsR#!D6&xAy8Q9d^!M&0Y=&&sMqO z=|0lRC&~bbnB0v+9-ag@NiQGET)7m~yd?xksDCKory8+m`tC1)QXhHp!q;Ca?fmMQ z7yv%5QCiugk>50-p0jv9l1Bh1JM;4d)&FIh~ufaO>LYHK7yY7FA(%p`b&{b)zOYIJ_MxoRF*3p3Xd|5inRS?E4E4f;bE1paz&HLCjgJ&Qe0bKRlPSGWI0nf-nv-Cztvj9x; zZTjoaSBHr5H`3pE*5xoAYz3x( zK+pJ<&IGfseZ}oq{yno66Vw{jY}8 z7cTU^pL><^4r77M)3ZJIVPmN6v?9CV1SisEocw<3GZyGDafBb}_~$VfC4Bk zUr(fi&=l#;=KK~Y1){!{@3p^l$$tmn0uo+;ToBua$lkU2)F-?M;P|RWHEq{y7Wy{% zh4=@|A)e$*m{j;_4Mf71b zEmt`0OpqDyDVGQdpR0pMUxn?*tEF?@7s{!`3~m32C(&{VN%yzSBYpz0d!#4M^` zgvns4B;Xuh0cDxUtDK^n;$dpRC=@CeL>05Yc!4vE7%dD0O<(``Sem)e=)@TI@>6n~ znxA-02m!-~>YWKDEP*w_2Zz;l_eZaL6bQ>xgniAp#eZpv5wj8Ix7kvQP-`Ag{5a!u zb10cG&|R|(^13860E6s9gF^;FQ(JRPTC~gY>hX136$cDvS0w;`uXSaH7=uPI(F+IS zShC1?!gPz_V&E$QbdiO$%Y!Guvo1XrkS4VZwauOD3vyuxH9Zo1qg8c8dKiO7NwMejc!ASb?>is1{W>k7nI zM*T70vYI{_2y3@`2?52MM%tEXP&@dM1Cj`tH54|OR!#x%8@QMWnh|oA;bnE@B+j6n z%Nb545b?2CqZ>xTEg9wv5gW(C-@^xUV^`4ibR>9{?u_aAKGxc-$)ueR&18w@K>G-& ztw?E>BC4WC~T*nVU9`6K3-!A zyAKcTFAg5;FYYgRrB3b6FU%AiCZ>?gA3mi1*5H0z$fI-(e}s703cCPBvC;fds;zH$ z${)0|vFjSbon?DV#Y%3DHLjN2t)B_So!{%#TsF5B6YSSVOpV=m)7@k3P~J64Y*tB3 ze0F3WYxO^S5&ruihvB!U2KQm>7@zg|=VrY?42~CRlqAVbCZ(yb9v{iI)6O4BtQ+@h z)DFq`56&Kyr7H`he*+j3faZPCn8N$+c)Fp^aimq!!K(Ep>4)r8iT1)W>TN#joJ~FC zYrTT@^=yc=d7)0eH9Tvr_Q%Qm)aFO4k~04ef^BXDlzRprM++66UW~V%X|J%)e@=z2 zGCa?jrTOGShQHi+_Uj&6@VPNLzIT`tuT|rjvI=Ts-c%v{bhD?yc&P;%hvib2$`Y=u zYLs3f$a(bm9%_f=%*7g(Y~3TTP3-U@(KL1EKnbY2;9kp9h)Y<_Rj%!!V?X(N!$H&c zC_K_68D&yUJDJuTwfB5~42r%r&JCvjY4^?apz$JG@*fdwk5#eP4otV&QRXwKPUqXde%*B-HqXvkmxfvh z?Uj2ilkM+%o0h0VSM;|_Af>Y1SL8o5oExE_{G5O(&K zG<(dThC5mY)rIn1j~sBn{eJKHt-qns&i?*t%x&lDdebHnl_4pjCJoN9a$l9*VJ_3& z$VTAcq2~9VB9a4JwWagV`bjzBvcMVVNW#}N?;SnA=W`XRb|O~IC9dvYt1etOOg0)Q zafnWFX-ZC)CTuTm+Qs_}an$PazT}lte&>)W6j=0NZ7)Va{=(hS+WKN9tDettDMI~F zPH06Mj12>iEee8xXZS8kLuMURDyhA~{SD&;;TOSnlj~_%1BQ~btkql<7rB5Vr((2Lg}* zi82&?bHX<+yD9FddppRplt3GvoUp;1gmlR~!~baPcCVC@7N)`mz-w}h@11wU8Fp&S zXZithBx*gHYURzefmM^8St27c0*A>~E+7(Y&26=4NXl(WJTI_~BwFxM6oXa%5+js| z6|(_v;$FQMlU%m;V8KsR%|%P67tQe0>!bEZ@B8y9rPT zK0cX|@|gj*%?x3(4gm%MSo!Ehw5)Gi?^2Ta2D3VuGF1rf+kOCQX|D5Q(u#Xv(vovW z>^!h|PPTcrG5MzYM{-=ad7d^6Tl8Fo>H1CjoUzK(huV?zy#}rlO^*Eh*n)K?-O8Uu zTD7mh)A|@YbE5gMhK1Cz;ntoU)r&cNl#ZcIkn5Yax4qIBWQ*f=V|@pqxfmaIWuK4l z7LTm@tzSI8V!36R)=kmm0uPc+vMBn$yT5(Y-aK(pH-1q#QJWSX;AjEgD@pjkBihe& zvtipk8{Y1;d!<5EWH5lJj-i{^6oo_Ax4t@H)scHxo8*6ypk@STz`wDxEtp}SBP4HF zDV?Y3l}o{6RX(RDGH?T>XDU$8i+1C(${BN`xq%v*-!t@koL&Dqe-9(B-5PT`;h5tq z6uFh~Djd6}(o&h@61LQO!9@a)SU_xjp(xla(C;+%V!-E5VpPurKX8=;CmBST*zaoX zsAag#?&sV9W-HX{CqM-eUMQM9uKaHA?sO8$uT>-lRriTC&vz@7 zu6gEJFdjldu*g(CAlP*kA6q@w1^XhNKRy?r#I#147g$jmya2Ac(j6_sVf9#5Hc(mG z{D2u_f053>?^UK?uia>EHM`O=OzTNlt6%6MT?UTG7HdLwrXv#$_%PE46mIDbFF~#6 zw>C}0TNX^>d%y7oc;VV-&}HCtS^P|oC7SJHtd-t|Zj4W%osi2l&c=`l9pZvggI(+Q z+77ML;qu(_;=1e-i(4l%>l=p}0vju$;zkwOyU%R|7CedSXsSrB#biZXC)q0(K8{`_ zH!$vvWVVO|ZT_WG|MW+62o;#B8ST!H=Rl7N&^%6l(rr`@|L!#3{O&Hd!=WFN4ePk` z3#B7zrv+Ngv*^+pM$FdH9ClZy-;H|O|D&`k+`Io(URU#mWH$>nYXK(kY(D`PSTcR8Fb7ztj>DA; ziMY!Tj7m|^yqB;OKUBC>Q)ZUXx2F~bk1v!ttPVn^_kNLY38W37Jh$vGAcM=IdxH- zkR*JoP!9c4*AI5^9ci7HOYTc|7a>bYK``KdcmU~RmDl(nPh#Z|KcmF}bc#eC2yr1J7NQBZ z?$>XoZp0|bvxx^{asfsv*G|bK*M2+zGDeD}dAx;s4<@h~gcXj|0~D8Jr~Qnj*$d4T z$ykx~ugU-#h~WJVd5mrMstuoHhzQlH z%`99L-ssp&--(Y@A7_)ihnjH2CrL6+CX-9v9Xc4{Xz~}-Z)sA^H-aH z{pP4g`?p~cEkWTdtk*0l#@+q=*AY|4rqW74P&N(*KF9dC5yb22rFve>OlJbR%7WI5 zee|gs1a}x+BA&wKxCy#QCp~9ezDkJ}-&SD_(CODymH;8C@$O`ou2cTaJQ`~g1jyQt zG5v`Cg9;WnkF)PwxB;1J?`rdbLCAeKPHTKyvu`|8VPyP7Qqx=fAt$8%Hnnlku9rOi z$P+P_uFaKGh$r_+iDFOFwB0|Kf(0ZLPz?x!RR(x$=3r z#*Y-mGNlo+GPfJ&GOKP}D1;k5*80qU^y5jM{`w!)XINzxC>3qx)5!HpF9S8{xd8wi z#PS#*Jzvp!MYcDR{G$wE>ttGQ`yArM?UVsa`kR8$_*lk_rppJK<8s zmNq{GG-xa3tur?F-9h7F$igxymw7_FIAStT^d9CsNcx<2hGe_4M7l0NS$$)^_k zw`~k24_@rgeRfna;%E?KNnWS{bY{Br3gE>WjOu!6e|5CXCkX#161HWl5rqwIDeibY693+ggWuuZ*?V;FG45l*MzX4u!qW+|Q z7H=)x?O^A<3Fx|R3y`r{WF$lFaQ-@)zEI!`XxIz;*MAvZswo6yqKz90{ri6z^S@jN z{zL)WiHvk7+25sX5+0ezI6RKzX~P6WSqV1Q-rP!!1OK={Zx7SqZ`6XO z_V-Et&u;wm9@xOG_pv4s|J04X>_k*rY8#C{1plzC{`rk>Rnw(&q_^ez9sbr;5Ud$8 zuyhegqyLy@1vy}};>O8)&Hw28-{$_meveG7RE@eK76JFS(Mab+sC#6PwTkoq%Q_LA z0Y;Na(>pHWugm5iZvE>!V13QMpuJD;&HmdZ(H&qebF6#t|M((C6&Sd6$KmqDzy07} z9+j&R+izUkugtop@b}(Ii;G@rH#}xO83n+NqF%_h6T^}B(2vyrLoc41Lhv!&xSD~$ zX>J<)x6V+-!`B`5=QOuB{L$90o??crAtHPkduJS{feZo(K zUjqI1=#Pf`df))N39EY&P*VEeJ^6pSK$Qs+VGBa;HvO+=|0n;gg@RT(JZ8o%>Hg3# z|H(a1^MV5`(l=l9IsZ0m|5KCx^}7>EK(1Q$%xdudOxHlZLBQtoLX3^{&$aQITC@(V zd%-#Pv@!p&nEuT&{HLp;62OAP$soq~b8Y;;pSP8cAce4iKi9^VCk-1fH0bp2B_Y+qO8&B*<^rA!Uz*s%W;6Xwn3;CC){fkEayYa9VESi&2v;MP&us$)`gq26W>C_0DNx2OytyHD}(^(mC z&gm(@6V8sZ`kz~lPp|)a3hPSAqE)BgTyL?Kw~_Mh^nDfZFbf+NAv4PHhIc8rgMq<+ z&WxhB@NL2*66V^lu{@l6y_oi=N|ec)J%ew}b#kPQ#w)d7jA!aujaR)Mgfy*Na3|9I;7MR@RX3`! zk*9boXtYeRv9p4dzyLrAQ$%e3?$12-k!r{9H;R>KW!t8&*%X{SN0|#d#S{Y^*%^^-o&KuDE@Twkzk6;XJIK z;2$Hx<}-CQ^?TW#gL=1BtY`tn zzS|=m{{W7E_sD)+MQII{dv|oib+t7Vmj4lKI-=`kOqYr2bfjZVdTN|BVb+vsADDyC<`D z_^%4wLCyz3_umr+dygQZG#=S!+cDE}rzV2PkLp`BK>{XZ= zM1OLU>RFx|h?mD2K8NIJ{*mL}y()w(E}TdsAa1?(sNw4H_}PDUF8k}!^3}r0KU(R? zvEc4ezuX?_+WW|Ln_O&Us_unW8u~~v{O7b(AAxUSeFgQ5Mx@aXp02?L*G`T9kG=Pf zXS477##^n9IMK6aDcZBkri0iqstZMl6gA@I2;_X!L?`hLTfg^3Ew)0<@M=O%80rqz2_;RR$USCQk+$?uo zjLRFo$@dFGkS+uSF!&mGwJ|K#HS9zVp{WB@s3u9YqQp7dtg_)hiF^avqu(q@9(chW z*B$t#QXx_|VDCTj)*~eqbl*N`#gkvXC1Qu4{V$@)h<}9C_jQzON1?ya= z$5_29G^mu`Ou2SS_i@NM27PXmV+5{2fjY+^UoiAJ31VATs&vI@o87<$<4vP^PEu=@ zue4M3ruIh)ca~{V)N<~6{Lq7yG5b#0chkwlYn|8mf~r3|4ddip#x)iLzh%geCNad2 zI%ZD_{0hN&Ab3c0psl>D2uH_3+hqm?F#d52*&5Lt{sr0bX*Ja*_jK@!T9t`=aT!ZP z+CFJlQmju8hTs1e;`z)RA~sWa3#_pHrq`+j8BCFrb@4l+P`kBN*L6^M+`%i4*zox3|b4UdP94Qfb z@C_29l`Gq`sv~hv8g339Ievy8XhBf}eL>HF8J%h48)!+9%D~JGW!3jOLOJx~ zSE&th!>?$TN>1{wJ^{6Jz1TPQqz@0BI5mGEk?yHxJQ2JU-{zh?Sa_~-q?EnjI)i?G z%prrA5IyMbtmyaw^D)__`urCvtazV(&}C~JWeHWaW3z_|Htm~ppHa+MJRZJG>P`vt zarH2diJRVroOi`aPb&7;IcHxmK$WA7s~@fIjQ_6Eb^4_Di-OtkE?C#-yXiJl%JIKu zJt4aEf&o=U!FWA{y6K*sSEfZG?`nGlBL0n?$&EVW>4<;mP7QQ9zo&_bkY1LFp z0p{}!WbE}rVVWRY7p^XiiIN3W)M5}y1+1gCqSsL;=K$n!|fv> zwQO48FKCC^5#VNy>G1vwW&fFszGs-x6v41TKUx&DG-=L0y4s>r7|072ekaE311~Ww z@v|Bej@WSI&{w^@tYP|JRQCF4$V=U&!atnVPI!rw9)9%?hJb_SilHh%CyJ4uIsFSs zmIu<^a?4bVhM?3g#z%~AH3e*FObhv{r&cz}?KI>e;^OUiKvD1S9s^=kY;44gtR$U> zlkUPye002*Z`rhrXxNPG#)be5o4gd`M!rG0JF{XJqT&+gDl90>)5+EoiPDk8B!G)+qlhP4MZp#|fCRjC??58Hy zJa%4{YxgT8tp=LcxlG3KXW!+z%l2erJ8oirW8!WS^24RMn`u#3)769Ov-7PgDpWi6 zUHX|DrNO%@Px%+115(3M9PH(_w^!!QH+VJ2nFr5#6kQ6t7TD!Dh3h)Em+!P251rs?|&u>FE{2wFf>o9Wu@Xmz4A76=mf5m9RC*g!gwf%W|^Pw6ag1G11a z>N{(9$W*8Jjq3KvdKve$ej zNu88Lc>9R7IdOWL^kw423OFCuJQlEgq`%&`?!#mam{qXqdws7>6yp{uc@dI+^W!2Z zXDcvEu_uQbbG>755q;Y?g%NG)(`o8bVGqGp@-jDUBYZu+ja3N(es}0~e}hOsgT67t zM`+W<7+qPi4iI;KO|X~zfH{&x6DAazkUZ@PAJ^@O zLjd~_>;Jq=^GurdT{av4?P`H>^;>C&gNM`lZ5vUyF#e_qk4?`*`e*w|6U*taZrRIy zo|;kHBXZL79p*5>;bZ#^M4sezPW{*41srAOOI@A5da4SaLgVi*w8H5+E>TzP04}A_ zSJHp(OF{GOmkCZp&Ytn3j@I(9$x`o#tJAM8%Bx4s_ljOjRA4U*cB)O33Ve}gg=Y)i z-EQ2h8Y1jv({(V{?N|G@?aZAo)N5I`h2IZu5uhawYRrFnGwZR}S<@f3Hy?43G+1bl z8};3v{uHR58@DlueDOm$eE|@t5-jzmPT%K@0Dp2QzU3$od~q{kQyhwUFE>%B)d5_g zD8V11M&;uHl8%Y66q2`W7t2b(IsYZdxibAxW!`-n=xqJwTFTem>sI9X3zze#8{5l| z!vtkCmk8B)(v4{>8;0$-I|eJN;QjD>yd$EJDiqpOhA%DPGw<{@nwMMl-Js6aYVjXU zb-(9!8oHzfyN3H7L?QqxQK&}Z^SaYzRu*=PHHp*91)pO;^ZT{~`qN9AGxUw>U<*ok zd8i{5(Y7f;%C{Im_H0omZ4%tggvHH*YXq0&Cb8PN%=5hV!Ix$E_-FS4Lhn-J>nyog zG*xUpD>K@{+bkoN;{BMKaC)}^S8ac$syyXmAu~f1 zboGV~HNhnsG#yptt?M)l*nV8H4_h^1cFR7??=I-hwh|>K0m)=LPaG#PKG=3AcqezI zr)~Aj24J`ufha0D>KWQf1X2LMz~(3W z6IrJ(5geae0nC2qo*73ev^+7Lw333G@_xmG73-eE`aNHu?nStQE``Tt0MY8~lN=xWx=D*=cTooC;{73^+mq zVAU>fdZo47UU$Puk<#I3yW03Rm*3MjdTlk2c{tO|(o$zlTjeY4jjmZ#X~oX5{?YG_ znFNomZ>PC5oA2nDo6RI9JAH;-Tzgxg?i$&kfY!~QRB3aq$}`G|KDig@bS$%>lDJ61 zsMg50uUCuLx@enX&Q63CYFuZo+w^S`MC_=~CS^6eS;32gmP{OC7Eq&GXhy63;k1=K z7d?`nfI%K4a9?@zGyS$>d3x z4anFFBxSF(L0vui7i2IFjXT_~L|O&qB$o;I+j1xTgqe$|@yXo{cRUU5Y_pOM{zR7) z?C<^vptqA|qPO^lDJVx7vvdU%te85MJ}Qo)dxGVGD_*0P^;lh9xlZZ~-YR}lWV&FK z?e-YI85PEdn2k6=MlPrV40a{6sej?T=Qz(nq=`S=HG4rthMFQ1&Kl&YF*D7=G(T_p za*FMAs_~BZeZ8t>Lj=jYbBjk2P4`wbP3*9Nj{_DN`aLTYEnZ=Gc{!-`=;>^+ zlhBKr9o=ond0DXh_M7b~k}C&@9lZmsv4Jx{r~5g|IL0Xz)!TV7EGJ-0bNw>dzVH4O z$~x~Q+fQ)pZT9L%f)KBFsU!~s%2;QkHjsL+?%TU$)0jheqPuFlT=}9d6s2t~5O{i# z&I7cPvN@ygG*!A)^d8c%>Op@(bAM9pbQl7iH26u6c)F6K4S(a>u>HUlYj=%{u?xIB zu0gbzV)sU6=-IJCSY$@Ku5AquG${WwGUF@6X?E%r%rp}7mEANt6m(Fmqzy4ZOzD5pTOGZ{d!;b57F%!&SukJXjZ8zo+jD|O`>PF4r=#1r2f3_4|p?+{QMTNdXo$GW|;mcR6`$*pKo;4`rNOk0)OUvTP%aA1$^SM1=~K2BhClz@i0=k!!(za<6T`UXbK zUhNXzY79R}I|#`4UdbUhpVhtJ*(#50VA}WCq36^5&<_1ikCx?gpPU@fljm!^Q?{^{F7T{=p}n=9i@ zdfW?ax&;*QeKHd~a*LLZ)VEk|JqUPQ14>!J-|)d&3%JD=Y8<75{^qF>673N)%X8dW zW>oYvTWv4L4*Ei#gA$=Hr6&;h-R6kJb<+)$<%q zk0cbK3AnZs%dFzBMsy{X&T<$1IHoh-2Q{hLw4319OVxxX)bH3>8&?LQr*rJh>eeog z7MN{(h^(b-YktFV=+7=PLu64};AHNG2?v)zKV>Il=ZMXfmbMiWyK#{9-l!hEr-q5( z#4081Tj+0onik=?OYWI25H?TQaj&+dhdTF^7QQ73-cFOsm_;jqD2U{6HeybR>_bwS z%Gu+Wb+^?mM2W1Aw4m4Fz4J3w>%&@vhX`)g@7niNE~4%R`#&D@aZ$*VLo&DpSDXe3Td?lf<-+f2$-xIDkYp_7(5<3Gd82#F<3l= zHLN>JUv#rt#D)7YIocTkCx~EhVZ@EQL@q>2S@4Fmr^dxBk3}CD$Tdg6M7s*pDo)Lp{1Wxy)-7(2psQrMC zQMfd+0&2&39SFS(!^V>~IvI(Rms~X%aYo+Fx*}TL5kq^uW8v}=W+t-V3tuLSK1K0` z%*wo(HJ+TF;++wL+1@c=fez^!XwMV@wgD>x_ks_nec?sws5p}0l@Ncy!b?FJc4CRd zfpHZRYvmHCjRsNHg5Kw0`H~dp|!wWft&G>e; zlndc0f_8b!Wayp_HB&WJXiV;`{Jc@nm%!|~DUv1=xE*p`dMejjW3u2Fx>tDPM9aOspQ!`|yNt zZt4d$9>UNR{UR>=$QO@EP&6D)q~be*AE9u1q;Mxb*t+{ZCdqw?#Uti?6?xYu(nT2x@*Y{NPs^-!%A0!lp*Mp zq@;=+e|5uk3;ta170Vjyfl2+ zEvCTIXVcmBxRNn_qv@`>&6#M17`~12ik78I;3zssgFQ}Z5av;JNw2%CU17y0 z<>thRA6}ZA=+B$QvxV;zbU^;Qn*)0wdXw*gR8^HwK?GB%4%01HrgmYVdb>!=M!NBeS^Q`rDHj!e@!-p;hKa?+no!OFE`)Wsa zLWma@lg>sQ8+xMKvwDA&ur&v+cd!{1B@kH4-cGI_CtXVSzRgF8-n$r1w<;6A_hYsW zMDGP(>Z$MBLBA|^^d4UL93meL7kj4wRAfR<>d-XV{P&Ox6We!}&KVzLMZ%XZa!Wz% zKk_05+v-+$ORpc^(W)h(d2O;!Lo(cC;w6Y<9+$_bhT5i@S~-1$rK<++pOt{#);%*O zL82gFk}KDnJNg{qvh}SebT-cEfLC$Bg7!KICeRE5PmLp_G(FO62(K!rF@u4~Z7MCc zolnwh+b*c6afNo~{OX223TeNRs^pt&dXxttwW+y^NXEHBiJ{ub%I9N-Kk8Q5j|0A! zSTzlk9G<|g&t(UqC8P>f=N&qu5D_9)%DO?hQJI?Nud8^6e3A$laU?xY+NZi#@=RrH z$M>FouCm0HZ`9n@D{3mI=8B#|b5|3|cOjg}xPW7b*#=yUb9xJ&6u|kE3{MkgFT^EwzA0{$?>C_p!AOB_(QxOI2t|5St*_IA;FG zBQr}(o>5F_B-&VCm$q#oZ)Ks3xUuX%SE`(ip-Xd=cakV~JA$v9;U$oqSy{kAS$84% z8ED7!PX4VY5V`v3V;ub2)K4U5sa^?aquQWufwmGi@hn1zFUSL}U5Uiz(UjrRM!Gt7 z(>`LHp7_uAi{13K`>*(~7;-^=b*4ZJBh%UGa7Z+Kr$KrKt}9&ce&5jBr$4_Zx;qJyF?O)2Kx#9 zp92RNEMs!43DTah2yIwzbJnd4_nnM;*HtAO>~LpDuwp0WiX&)f!FXom6+On1AWbTI z9D{bCt+L%^1+ni;VrqQ%iW()Lf~G}TU*%@QulaSZ?|P7BUfanwH+wGfUtUR4xU?KF zAHE%veU;6aPqTy&f*Ddok31QbuP|d9Nxy?{L!f!kmX=l46yBS)!gYE@T8;A!W;cW{ zH7b)Hm{5ndhGJyPexvs(EV{U@JGz8df5pHqxQ=-ZZfw%Ma*ppWVfhP*ia%Vv-f3tb z4PWS3r8$A$zNq)nVY{kdheByUpMEoQ4bZk@?b4x$ih4rrnn1S{GX;r^d6JkHOG&G< z$7EDTnkIL=Uo4=t3B64gU_{5R=tg~+2}j|Mi$~Korsgw!MTd$6^tjy2>NwJ`+$2OQ zP(ZN{FqO*pxVPxG#w1`k82f*X6>z8M zqY6=O3CZcoNw!QPvq75BuV?9~hJ;fLC&D!i(?{SH@s zPq=JA?rK${U%`>oi2;cxCDZ!#x`gU5=h+qsXoLFTxX9HDnGVn(scOfC@H-*#{2C_M znu=Dhh&mqv)@qC~atGxS%@+WFGB%5zhrW$lvbx@j+nx!#y&}ZQ)$Pa3#B+>1w^?j* zl-8Acch|xX`%(`p@ZS5&Y5$sdRR!K^v)rmxosw0{H#fiZB!k$5Rwt7L->Ys1IPR7% zNfTUyuBZh27|QFnls}ti-hE5_9vU&< z4rM#VygV$kxj%cETolt#Vlp+-{sOz*;nDoach}NI39bO`lL^#P`YIZ`eVN-ES1|uQ zN$_l%d2_IKvS7h>+ahtic1(`7Zyk1!)>|{O@Esmj`$=z(0qDN_;GU4v27h41fQh*z zEInEyN|^y$m;PaA3UJ^~HnE_6q&{J?KA2HfIQbVx0nPPV4dMzjX zoc44&12F_On!Uz)b{$q?zMp$Dt_3S1!o7m_LG_e4&T~m?>%;s_Sl4Yf=GSFcD}U5YQd^)5jjX*F4_vbKa*_`TSIl4 zl7>S$U4jVJ%|W}5jLo8JmYpWbj}|RBl_$7y3p&!4munAQ1(U@E) zoznl~5x+jIvHb1?p;;F0HCM({zE8KBF^ z66U?_F4Nl9U_&&1X4o-lqAC5@LEUY9O__^1vQyyH@wqC{Nz7S|c*o{>B9^!E!HZ2_ zfG4>#h)t_z;$4#8B0?h#jf-7@`hDmuG?I1+TCRhtysSY8->dMlBI7d-g3$P8C37gI z^Ks;jL!ItJNf&NYKo~M=DnjrfrS`n}6m` z@!Sr}rsvAquOP?!$A8Ee^GngkhmVRn0PdlHlx({2FG&+o81B22&v#C>cQwe#hpR9H zDf18PH<~9SW^xlH@=I&9<6oqA8*_EiE^*({d5$l9jS^cT#rKpu>Xt$Uz2UYCI4M2) z2Z+vDfzI-+`v*fSy)5d?!g~_-s_d z(Q7TXFKIIe~e=96KbgG`JV6IWFd7B!Na&1XbSO9=Wtmz zZDC}dxQQcw;XT~)cstw!o4>q$e?|uGQKAlM^;_klm zmuT1#x+`q_hK7l6Mdj}zy$PQml*l9|+`S){uGZfTp-NPTC>qAG0TTJ{$zRWvtpUVQ>tx%4L0~1Dz8%Z!{p4 zc!N;;a^ai)aCibIW#c@lFxIzq)=ag=+uIJO1J7O2^8=?qz& zH_zKK@#$+%Yn!%y8JLmmtrkZ&zAR6Eg>&*Qh%JzV@s1k7=2pvNW)d9v0w;FYk@{Un zFQtixi3Ycx_a`w-ze+Z{dEjTI|FlNV1vUWh9$5P@mE+Iz9EWw0I~Z4MZnD_F);?TstjvsI|3XuyXyk~LHnwiF1PYDcI9l4Ts2O{b#kqklotWmE zA-(+?JDphE-!+=v9mybPAC9)!_#|s~!s7jCH=iBT?D&njCt0%1CfQ4)5GILrW-lBwkHM$jA;cM1jwRWATfgEmkkM|jh@gIO zCnb#wS@9n1X(LrAe(ud#wLSG}l@5u5>f0h?$Wc`iT>i7&n(vFpH^+*1g^ zbu)lTP;>On3wYPz?H$c5s#T_7{iglRgnq^u5Xl)7C3Kd3BmQFGn#0*=kykx?_LGf)FiY3=CLhs%J1wz!XHm{b^kAN|@yP^v5 z`Qvs2&Q=2zozW}cE-`He@8aBfY5lbNU~x41XqR!7j1N{+fp#!-r6d`jQ8$t(2vxW> z>U`=>tknRcE43mjv~tfa6ZhyLxN0hn4SO8Nt^C}t(nfl z#GIyMBKv;$VML|}0-ryt#)4v`i#)^`8h+&9teGk;Dzc^;Ceqk{)1yX<8gG&HBVXah zyfiFc-qt_{!8S-XrSZh_4+o|dX%P_Ed{fQkf?LroZ}ZKNxr`A?@dd+=cU=T;*3~+7 zYR>d>5iqTxWomt>574pgOWX@4dK6*6Q=&bZ8;E>eAy|VJ$bj-Dp&Q5dfjEiaEbC=msRN9w9(^Q_{5FhIY9Svs6BPd$XM5=SdWO+6q9&Z?NSjt9olNnw ztNhw_BYq4lqsL}QvNWU4`gqJB!upa*gMdzWzY2mKX1!PN!B~=bxox`ck}hMnSzXpR zrLBWFLl*0Z&wp4j3J$!#RJpplOI@e0bOhZ3V0Mg}4TMwDu28aA^bM&B)Vj11=)P3{ zn|@CZ;;e*AiFrb@=KD4`{ZgSpm*^XY-`z*Rg?wNa^mvk#oCa-rEu@7h(|VR+%}Ej} zrK>*(PP_T>RrTa8ia)pHx)3k=B^ffYRr?8(u^I~4ha_3S^RwRX0umT6h9?8M#4h2R z!I8cdpe$p(Ev=P&Ka5B=AE~{va5g%=D`ZLKB7i}hwYqO)e?IbxVS~En=%Ck{NegBq zehIINB$`oldTYE)pVDM$!#`@qqDnfIXBKPJco-WO6#?-%)6r&3H&!M#bSQvvCXU|C%NTw`^ZqT=I?u~H`6hRA)64IG zJ%}JgMa`QX-0CqAgT}fJCz(*)MuO``z%Mi=)3&J2*^uOw6UpAr^yWk+Qfa|`JynSS zP0spYcgnp@gV_K!(9X4FQI=+?wRUVgbi21)d+;N@#IMc3&sTsvOE1?&(}D+?flnqn z(i2abroLU(TmdjOe9qF@Q^AG`!S)WBd8=FGBkxyI78zV1#y$oOP0*QJ%)v(RsC>!p zg|mi)_W*)@KUq+G+w85F88=^=@Z2ucu=s^ZxFg6A*(Z`0>Is74kZ1!5Oq+Kv2NUQ0TUbE6dny%5@Wz%|Wa7D-mPjn41c(NzB+iOX1_@BU`H!l=QNFH_=Tzu>Aj zt|5l)B9&=z_{L`a!Q*>oqY^)2pl!NqZXt|Qd1^4? z=G@5G01ziur%ux*4Q`uU!oVyFYzHd(SzTw6F)swGt|rHe(iCA4Ss5MfHB zkx$+=)I;e?qN$qRJ8@^Md3h$tE4_*xtMENMcV|{#`_V?>Tt(!fcP^{07RY;HcDm3b z98!SuOer=m*RJfsZXeYHcghPU3}tLCFI)G-e!x*gyO8)8zE^svk`DxGNuY2X5>Vvk zsyv+$j9+T^*UnQ*#^l2B#rRqp{5xE~$gOF;rFf0d@U8;Zb6H z@zT@)AWzAC!E;-Hc7o*I#@N}+QSHWaFFx?OzVuG1VMi!Uj;g}gMJWoDxtvmt-mLbB zX80a#S16C2J4t_X({9R?-EY0{y5G%YY65~iE%WHk?zrdj#o}Q9&*TTN;r@fnyW#9D zPQQr%`LCs$U$l(|$HO^z`97KA{BFb3pmyQhlHj%7i>f*&L)Em87dx zj6y9P0P$Mi>W!h5<#n>G%X;|qHmyQXH&K#vbDV?z;t42kzFxVT^#hHGjP7Nd711us zQorDN<(t-CN^YJ8ze8s%SyThEE@V%naqYz(+#q`d4)4w!1aNZfs!3(>_>8~tZ>`&y zh4rhym&=V?8-nRfBIQ?}E4UYKWa)J5Nb>h6NbUl`r7dvRbN-h)GJ~rIq#5#~O+1Y!hDOnnzIcXrmW67Q zF9gA9fTS79@|4@1EXP|F_WQmCK)Y0U`}C~_Ih#U*dxjm=)ExXPLBlvGg#ocNRD81! zmjVRTAx#THFKjHZOeah^f>lSJwT(wZh$*yp$Qcgq zS^2e^8UO7t!#ifnX!~F$CVINMFOPzD0C_*D9F2Ygwt1oiuU-8O+PH=euH%sYm@7U* zHjTXi$NMeZD*AHs)3_+*hv7op z^bTr$qB2NVV?=kQ(?@E9RW66Ln#h6DsB=J8pb=M_SuIe51*KToOdFMoW~Fl4_7WFO&-bMA?qVeRsULw*tr|(Cz(!acdo4 zPn#|M@zT2!6FFPTys{LfU6MQJr1m9_(|O)0l_6UskZ6GRp$40tF{bUzM@$GwvZ5(1 zE!61t+3OtEb+B84QlbP#mn_E+2%iay=frDf5g^)RPD)6w@JyZKfnWB81njE0c4bS1 zF9fvhIenIrQ+MFe#k^EwL2m1-ZTn43_FbV^8F3=Szv4yL6I?;iZd)7Vd7bF;m+Z|T z^2gr~uAemCeOCRAZ-omX_bkccLcXSkvq{mvb z9@48+IT{4^k>opje5t!(vLM6J3bAeMBO>Yk_S(EnAM=5{bF(eS*mhiEwVQ_VXRSt6 z#(sZ%w9u(Hd9pfPmkf1?-5lqkS{Jp+ug&(Bcszh$pkzdantegd1gp_uXi1kgQO*@} z=^+Dd54zRjiq7BcsF}FYP_-c9(VlK9$rqF;bt*@^{VQOqSN!43bq2`Io4-SLes`rc6+@ZHgivx9Y=fDj%%ax?o zn4QJs3q9Y*Jz@alnDpey)S7+;U5;QT4k`HL+QD4Tg#d1BJrbmY?(ype;;)rWK?f$Z zSs(5c5meIr^t*1W8YF!3s{fPq_~IkQxV`<+$akT5>#=*A2y9>w2mYOns6_6>)&n;M zpfovbY*xmWw6p%~E9Y8F??f8T>xaj+g8vWXzh4v2}U`+i7D9cTk}^9W%_!YqfFU~bUc zXo@g3;|5ou08`^RXfH3a?u)7SXFvSPGCm5{*G7q6WO)`fwki0E`m|sl34z~7&s(VJ zIp2afjS_OugG=L)5hqCOLa%g~k zVFdB>a=kweJU1Kf&~i|lrOkdLSTHe3A0mhoD% zus?Fxv*zPLZ95vYC+YGa+}P#>17u~iI&Yg~PfM)1`|#xw?L|MsF3@r5h~km^x0Q6` zk6*lq;AcyLezhdGZ(P;BQXNRO`~A3mGeu*nJk%N_xYd@3?wc#jlou29L>_sLlcG{F zp}(K>bk(-g20#pUBfE1h#y^;9_$!Q=>elA0cQbLZ&Jl-Ka`Zr0v(L?Iz|tzd@nH?-MYDGK>9 zCbR+cdwKbcBM*a2a6U8i0yK7*VR2i76kt=5QzMdS9PyZP7_2+|_SqDN!W%U@m@y!;3Oc_ilBxK&!Ou9?z_S; zI2?ZFtKQR~jh!x=&8+f92_*}GRNDS(>Haj}P5T`A+Tzfx9;SkNBZNrvyA!!q{g$@5 zjx!K5F_ahTwCBUVy4&ydlBfyVgV#vz>naI=HvcJn(<~}bVc!Ac<6Rm14$32KNU7oS zo>VWJ@&A?eoXDQ>!~&_BXr}%#(86z0`k2#Jo{>{kny^{T7yk8cpGy9BERbGb~bjPES4^f?l+p+7< zHp#xg%E}8Fc0}ATt0Mb&xD|%F1W910F)n#fL;mv_Iz`I}2(RmjC1`*+%7`H7b#VUd zA|HL!F7l<0&P;*$O{dDBpd)~{_k#DtbBhYvkjwt%kiAq6rW;+gSYtM)N2|Bl23czZ zfOHmvBDYF(Y?m}-&beFu4-b<<2L!9lYM34uQuNJeIQpZ#PgNebIpXHt)egbf>~vH7 z`H`Dal4a@*d4gk|2+T2}8<5!FD&&+S6==%(N#MRt(V`c3bib{{`9%X~p|rfnN6<&> zR7aLko@wV9K8Ld97e*B^Ew?*_J1l5$M+?*4$aYu*XWnvS zvXJ8>+r7QXhnZL)0`Y_P)4H-Gx(Z_Ixr0eYs2{<1pZS2-b=lYAQ4h2f=ys@}5YVF8 zdH)#h#8FJ2QCo@WLqjuw_<|6#aFHL$(P9X6q+|19jB=AmZ=JEvc`EJ}5bu4XbYrM7 z?Jl&jr+SV8Xnrf~!(G1I+?>pno13L7zsStTNF=8wHZo8`AA z1f1^gg{oW&ubuYy-Jo_m#HqVOJ!9W9)L?MUMQmWUWw!L#K_kyhIkan5J2Zc9Xun}U08@F9$DL0xR76m z##56MBPMp60_;^H$$@BlRw*rLT{Csgyg7a8ThoJmIkx42dcXC8Jeww>T4=l7d_BQ6 zDZUBTpy192>n&R4nBT9OQ_x2>LWgy!eV3vqWc%`2|Iodn3TIw2%KC`*7aO*+h1ER_ z={tC}(|*DgV3ryPw>{(oYkxy(NT5+N%aZ$9Cc2$TB*i$LQ0KHIF;qy=Pn6!gnfW@* z5tOq5BxnCtocUVEnZJ7_d^Bnj5(hYK#0vA%g&w0{BSX8;ba@#|bg)!H;AUCS&Mw?> zv?sjLh~A{lcGB3RP3&Z1aINH(+2}Dy!|c|aZ8NX{1Er?h8kyA{568i$xP`uBoO`N! znXh1)^16YrHP7zBfKtMP5|Lq}Yr*x2avVDV7q3QJDr_Y;hq)}b1?@YZxy&5#hV8_% zE50Y~OS+TO@k(Ys{C zH)V@CVgEHWqY^c_+_~jTxa93&z&ln}3A6*UmY~-h1!MG4Hy+6>==0_pI@ZXfTGxOI zjB?&Cy&X)FM6)rF-nVWZZ;N6)=2}Adq?=cQD5y_-pxS$T!2mElvZmq)MJvHW>;ap) zZ#2l?Y#1Hb(chB_;P1)>nb2y+sg6({HK?GDv#i%pJECK780**o2J#pA-{Gy06;}w( znzp;@X$<{_^~hd@bX#g5&*&7oyRF(Q6$)OM7_xF8cu$OOjLI885PJ3q`t}_eEuW|- z!S@sZ>lc*SD2xx805UkyqBKIVBaq= zihs+OMcD!7s*3-5CJ|C;@-~p2D`{)0rDAk|A$sKITJmrINbLT%SN`*#87UxL(cFd9 zazYS!FY(PxYJ1fi+xF*wYR>$}-0t@Ph|<)fN1SM>-_%jKBee# zTDPl!AV4cW=dRpfe&sN44FJs&-4JM9>|-V%LZZr@CHj-%FF?6}+sXF4f6#0{+_wA| z)cx-}bb*1+Gcx7>mM8v?3;*eXqAxp8%QTJ3yYL^Yi2i9s4Mq0D%B@FZ4gcQ?6KbU$ zRz01mYs3A=s;56q@gFJU^Z=}EsAwJczdM|NCL_JK1zwx-T`6Ave|zt-5>P*pT`Tdh zzVx3i|DOZ=&!1Z}0tUW$FL<#lIT<@1y&(z54(8=nnAXsUPQ{1n*;z zFY24}moHxw7QE1YmCdO|Uzq(itCpltGTZHzmoH(?Y;4ZOPwPVs_yq;yc+8H!haP|Y zyIY8$F00BJL%NXH@DkVBZ8JZXmGZz$} z9y$6Se<&u>KV#_F-7HJ}i+}o`k8UXfYgvD*y?@3&{+-4At@i$Hhy2R)1Q^QyH-=$Gb!Qau&umY5HXkyA z!_Nq@aC(4^Q_{(RwC8p9XD-Me20%9F#@|Z*{A}Go@p)0(Gr`#`MgIxu#%57VnV${m z3_M_C(^AG5e>MVllr!qg^}Sanp3BsXvJ*Gb6MEL2`<8dn01#heURWN4>6^)7^lHb*dP z7CSjOv@3LVbqOeB--&u~>CP)VD0IzV$ZqIm8`Dqba7fTu&WbsRab1rl)eH91~<><*8x+%3+IH<#|(C^h$H-_6}BgZ^+Fk#T|i z3`vCC0oEq@NT2jGgJEQ`6S`0ZGMCrYS#IR# zTJrt$QN?Uv>$yepgMONIh$$P2L-5rZ@=G?Mz8_N^rE}2>sVq^?1?(f!6VQ@PCfB2! z6=IzWCH1UT9$u*Tzuw{%@Xi06j$0wD5<9QMe1-1WTHtv|PyiR$#dxzWUtaTbk=`fC zpJ#CCa0Yo8mp_>S3yOpDRgd0r&?JimcI2!@L4ud$o$yHOxdKSP$+M^l)!eW zq2(GK0u&M=eCB8~C;X;D`O)E1zdr{8VT}#p{o2`gtQ#@KR&9Dql}Z`; zbZa~@N0yQ#9MQZN>nGn`({z!5mOQm!d-wA=A*N@6VXoXG{JZozeri2h(HSMqSK!v0 zrJCm>e8(d)gRr$W=|9uza8dl@@i;NFG7Oajmfj%@ah>tWGqhfzsY#yY_s?$(QEuzA zy|ICMr+%XknLXLdcX%egW3wlJFX6dvdE447{}iX5`_Ruka25&hKrq?(&VLX5&mTSh zbmfgrr?f;2iY`p`H@2>E<~fg)+1GjNNc_&9`H_N$q(^;EJbB8>Z*{IeH}}*yyIN3f z6<+42`5pFEx&s{RqvI$^r=iy*q0N?Ar|WDb%1;&Bk@upt zf1@0+(9zLhujP?&(6u&`nrch@{ZEz=4J^ZZz*yyqnH^X$NhG0Dc&wgI*eTTp=Ao;&Fg0Z4SN_1xyQg&!#$K8g&_@ZBqp#t z$XU9B4-VEx%u@>&Rp?~MEtfjKybZ7+5|X4{r|DDL-8S5Zk)QCMs@TvpJ;m4viR2t0 zQF2B{*%qsY$RZD1r&36iP%dBwjEw)J}O-?=R0V{18DHhO{NiVYMjt8$*DqSb)!1MhO|S zi~uLEQI-c*#_PMH)WY1FTKBXrXERi^Iy47!qSvYcm(&p2loa{VVxcNX?ngQutvA2KBSsov)rn%mhy2VB1&{$;FNgh1GEa%3?_ zl}zUUQ35`8$@Yu*Y=PHf_tgt*9KL6?ryA2E^t$B80!_;^^PN~13~{&&1Py-W425nt`XUF+$h z4Ir#*9r$W#`a2>f{_|;Di8uA3SbZ~JXP&IO0x+@4C{3X zK)&Eh1MN|)tPGh^9)peI7dw#_O{ceN*NCEQYP*J!)DWXS=^Ats8s=>jZ3%Ehhi^WVkI_ZsdKduHk99arSv2Ha{^ zf9QJ~d-pXGn)981pzT`^>oaCb?1~qLM_LNVO7N3xGog?Bszw5^6JRK11{S=A) zx|+x(@>A6MNg;!HF331hc$_+*pGUxpB`_4v+D1JIFBvHsBPv<#qyZSZgNaud+O05pnWub(xi5SKcbC=hwWa9k!C#F)N>A}IzW?gYZ zmUUFvr`og#Kky;zAE&1KJ;21{k{%v}+Kd1GLW$?t@}asP$s| z!tbfI%JiCH`N`6r8tu0|@IXv>)xjk-0dl}^hUGSnV_)oz$_$`yDo zvU=I@{q)#dN*5PB&m0__{OmgE(8dQon9NV}{jj<}k=Y~E#d-OZpF-dVKJl4R5p*e% zDqqCumfY_PuI~kVOF5318NfK#do1sj>h1}=Th)jFjUstFT>O(Ee?kz;OZlrW-(HxZS%SL_+XuA?p}`qs?ubV48< zJ@qI^qMp|GSI@znwN3*_6a?ydJQp;u8@;F&qZrT6Ub~K%i4sGuDrFicvnvYiG!qTD zjOc3!LL2>WtcGTKbJw0tA@Vn@tQIrHkrt=>nPQ329D3S3F!qut5Jz&Bej$C)oLu%mS-l?>xlA2#of7YH&!F?)(8o zRn`|Op!!x?1i5xa|M1_~RdnxB<{9&j7w0DyK)~3e-Q<##DU8&1y6V1g7mz5A2ePsc8x1q>YGB5Z9EH;P_PfU=Zv*=3&0!G!T13TB z`X{CQ+dRYk7vN-GogNjOdkuMIi87U$3^t4#8rANRvT44L5Sjn6zY__qQE6z*Vvv4# z-g|>awuRXpY}!%Dgl)Cj6uq=$%^}l>I;O zfrO8&3kI*MAXp8*!kJIXr_OSOKNw!t1%k{Q}*m()=v1 ze5^b2%H+$ZJ*|d556nU8FJDiaLG>W^2EH#6%20lQ?;;E=NG?JxA{bs*cpj`}?^#K? zIBbL9tXrBs>|6fKNw-^u5^5lGKwZa1%|&lgAR-TBhQ5nui27_psdKH{o(0;C?%iU} z=;nx~u&d$TEZI|R(HlGMPnR2@u10)NC{Cmaqh0u7p7h0z-&>1geJ3Ysckq)^I!fe7oOPneap9Ig87`@9`IPHwSV2-BcxIW#b2Mc& zD>Wiy=yr$C*=EcZ3UM}Y1z+ra_r=m!9+2RM?&^(%+v4Zj*;U5l3(HAf`QHmFD+Ozh zY7u$X%d9rx`tAoq8X8ye7S3buRdLGM#1@kCvq=?l#XewPzzH5Lx3Nu5LxxP*t!dIiK&eWs+MI zYHn4n=kI@0f@XhTUxH2PYV~N)myFsLT5I~4wRJG7WH`I`b5w791b@G0?=6Y(2-v_+ zmZBgsR1j4mAoDrW@-<6wR{hI$+b6^-Tx1<2F&awjlmpT-!9@jfAW4?esEInx=v@}z z4`=xjBYHH++v0JhR0OyOW@+{?r7+sN_u&*R2pI=RvRIHSMih_3r(XXfApAg0`1f6S zQ;))ZQvbr^x#3W@+UmE=Bu|UBj8Z+fsfS%jqSclLo6`P=07a;kdUZ26WtY*M?U|@e z6czQr&q~Heeo)2txHCVAomu{wnbfIKZ0b*JGT3aaRq1G2wcE}bTK!#_oW^q{B^%O4 z<)%C2YG6AUVW^p5nBp@UGedkt|9hPB83YuClL)1Cj7aK=Hm$UDM+b<|cGWr*u$-2= z^TM`c{W=pK@*3Ewnrk$=cgh*uY!4vq@IBQv9`7v8F1 zpwQYG*0Hs5i4UjHzAkU_SL?Xr&Qmgt&8qX=j+`=ckSdwJI4sxLTwf0#e`7QWO^(zg zw_!V!N+k*mmi*lY()LD6?4dzjS;QEo{759LV4d*&aBvi3cVq*gDQY-RCq^M$S0qy< z>h|s@>MwiGa+neXi=E~gaYlxs(dee-KrR~GN!YT$P2#}4q9rmsbr7p~1O!VCGy;;X zj?`VaoOT#o^yg)l&G?+{X9(0XPkAgSMy)S*e)%+XG6F@8jD7y8VE}Pi)Xb&aOEP&W zPd7D_{^+UW-M0aABuM+5ys~yUKB3T*aVR^B3O!jQyQf8+>fNu&btNZB6fPj4PPv>HxR@}4JJt+FbxQj-aL zTfPt>Y?jK@`XdBHMsg#%P6|1DYn!89h}wa&3EOyT$bMh@9EEKKT_U)F!MTM*L-nww z-S;|GEPs5N))4$+I#?O6|bhsh&XcR%zQhYT5KeH*b; zZNx1HF5cR597GlB+NjQfM2Z`QuFdt&KQX4`kVxNHA1gM`Y@_zdeTe`;=K#9UP~ot^ za#mIBxb(#w*xD^H-_<52pGTXeBl>CwGmahXiMPq5<7eE?&-#DDge&`VkKxTL0o*Kx zMU-xn4lN4ndc-&*-t7xDP}d^dObmF8bCb|_I?>BCIN3uUQ+_OcMRHYZ1=8n#yyjj@ z-2GUerJUM7tYe4lk5I6}7dtkLI~uIXhLlp^i)>3|Q&n=&lyZr?9Yz=L4YluT)SarC z)(W$7KioNjpVel|%~Xs~3`FLiDvv`^NC0cpm|Pqe3Y%`r5q@NgcIp;nYT@hj@Yu96 zh}YkX8~GI}NuEzVLnM zx)&QkEf-_q*}9m9hU*6?>|8&=J9PhD8GSE^gJ;LK=QuBzvTz={JhUH%*&sr_pld2H z*?2p+$50*|Rg1{nyVwJ}T|_v*1oJsv&C9sw)55C%LCn4JdQNvFf&ONebNQ5~4mu*q z=?+!CjJRl-T7B<_|6EBN={!_49?osjHn=70Htk}b11VZaszyAAooy_yQCrz9dqqO* zEUmJq?S;1MuS`@=7Tw1Fidvj&T#2!{DkYrN`lryGzE|W)(trsjUIuZvSe~W-h_Q1C zNF1h*l6Bl5V%#^9yLOn@8c73FJn3g%ZN`UbTD|3ccMz0JXp5+;EjF4GlnBe)!da$m_cyDC6qK8WXqXN27HP zIa3C$x~N*$(evErmMO=TFBAG1;d*t~hn#6Kl~lT}^USK6O!ygw@8J3c7{Ni4tVi`2 zM+zqj?(IkG%i2(mivNUqt|pzXW)rmc@%40JLh!8zm(lG^rI|wpJ=`$`G`4yT#}3ndO;{Pttu%(9;Xj zjFeS~Y0P}i=@VLM4s_AFQI5Mm@r@DW5JW4SQ-^yK{ny2h8}_S4YV(J1B0n)I$U^r7 zpr6Ec`y$hHY%9~d2=PKo>rRIX6vZnKnO0~0@D+K@yVG7ocauYy?Y4NsI%+>jol?Q1Lgc0AJgcfqlUmb;g`?8=_Hsw0Oadl*?@+?VJ zczmv?jY?<8l|m1cKtNtr(!Y-;)qv%9@c~O6Cn7Ji5Q-xk7jD3;e_X~&B>90-QDF;( zf!azs@VwRr{^LTtr+VfH!PuU70Xuz95a|r?Pgs9KXO9+S!cVE8=R2Ly^}KRL?kRgW zIqadY`B_dNAHm${k2W%HIXSv+oqcU-AnQ^5y%snsQ=^vl+pJS}+z-dh(V6w{_C_@Y zv1LMU16!0-t+AVIcdCLh((n(^MsCsS0HnzO)71ks@*of|^{Pibm$ZV{R^@U(D4Z2< z_q$HmjELWI9=XiE-HV`+wey;o$R0-znJIN!G|dtgo{xt%$m@~lFkUhlK!I=Ac&TL~ zsH!4P7)rAk$Vk6}FI4!CVV^)d{A&B~Q^iT;Y@qDa#Tcn)S}td>+MuoKqhA=S6(d1s zfL-fwcy5p#GDDbc51WaU($)*=;MsXHG!W;=4BPx{YaBZ;$Q6GNamq$YYD@>0{0W5a ziT;lFFhpOX4a?&Vvp8?rUJrl>FXeyUyPxq~AMWkp6L;Ttf71>sY%^MzLeFj79PHfb z74gklXEjM;zj!D|!<gDD@g7NBlLt>y+VRl2j7y-z&H=u0)4g6sl zEpgwOzuYUUK#qfj2Q!HEac{xTiE~^@3CaM;OV!QdJ{R8_*i&ejq0xSm@wd&)7Lz{^ z1Om$>_IiDCVfPRJbr*m)W)*_AHwFu>e4t=5_QQfzBVQg3ZOd4@q*!(cp-!%-89sVWz(3a+;SDjD(?5AQpQIU2z! z=U-9(A^$=PGVNG_YQJ0EOTN>9QKA{gXYrE%p%tZ0wX2ObFyDx2RU2%aS4fsqjs4cjJj(lwRRMHdC> z(M7?$vcdJ3(&bT%q&)%oIC!bl8PW?Q3og-wZP_H}G0JS9m?Tok6E_b0=D_*M9>p*z z2@yF9R$t^-@u%oY(*PsH1${QK&@!iJ(fkwu4;e_+f2XAilK`R;p+FDQ)LrIb#1 zGOWUt;7CxlPhaea5y%x~VdT=h`gP_B>gU-@o0;|_GMM-wGp590j{{zbwOs2TNk>2Y z| ze~y7x{@eB_B$J!5ZL(o-TOIzFS#uuD5+mfW8(K<5a4`maGr?21L_>VB8NVV zp~KQqjxE;wa^=!L5U`|lLA@>*dRYX66p+k`Z3&jt%WNP`Yk9iA3akk;o&>N~bY1ZR zJvf1o9mmPk5-gy;`=7wVV*YV5epMc*5YwVTTe3*4KlD<)$GbVP(jL*NfY3v_Ac z7?!eoph+s8qfwv;;=~_~FN?rue2UGwWI_4$wQBL8T=HiuU|?23cI{i3X6$vmJ?2&` zzkn7ABZu10_yv8KhX$bsg*&EBTTC$|!1BoAGUzD}z1$SLba1i^E5N?FFxe$ABckwy z=e{}#mXy9BI^9^goYX-{|A4%q5$$#WFYT4@q=+Ut6Y;B=k#O zE0aD5W0Zbl5A2jyVWECjA-`d)0j~s&QZc3mU&{itf=dwYI1F|R`3CwhlXwk!uKZ)b z#(+>VPDKg<+uQ^Sz+K9+;nw>uX~>1!yx&HcAFeEt`sB6!@%9|r%4k&-lC$OBB3_y} zanZVZ5%jwkl6WIQ1}sYTn?+U3t(_+MAr+u(1=rVInrqaQuDAU8@ji1#5PK3Rv&0fJ z+*OR_N^zseH4)nW`H2agTF1XL%#nI>0{8JGF2`h(JtzW} z@|{6a21ZKYnx6zUahUnfko1_AjGEbcqz-jNGLGwa5*T*C`qlq%4+BcZar z1)IF(o;X;H-JiG!E7@SF8Z8N!*}Yx@U(d@;70R{tMm}4o2kXTz)Wgik8**!YfuJ7E zf;ENV+-oh91m88C7pCxSMDw~}qY*D}UXgzPPq_lt>kf-G>kG}Avap#M(OD;m{Q z2Qta_00)c_Pp8U6cH7=vUWAQ)Fc=x;sm`+3hui#VWG1j7qJ2*6m>U8>fE4t^)vIIN zgpe?^=?#U4c`dDbv^QmX;t21J1k8|4Fx$OdOWUcy|`FHtaahPpoddMSEV ziGt=Y7(5>RZmcf(USDy z_>6vnuRvA`(~SSku}aJU`uAaYarK|i_zxrd_Jb#)m((mC{N3hYtY?J+KyJ8$E7||& z{e5u{Jkejnatjl6VJ!NeGylIz@_&`&Z-o3mrTG81O5(c&uMGcxDWd!HIWiEHpjM|yLDX|UJ=$5apTYPANN*NNx6G zw-+|%*cT_fs030@echwM0@#TQ!^~h*k<$a*71r9l;W~5;)Zk(_#)VaQ&Y!4%%WFgb zC^m7CSQBJT(G0~AoAX?);{F)aL5EAEOUWLf!Hn-mis`Sc@U`8z`P|ni{^EX5w2rOd zU$7}DjJWsT`gc>_cgc4wcdL9YFw|BtCdt#3S(??__TGoq2-KS1nEEd<) zERcaNQ`cAgSYb)L;)$-EUskhvXp*Kcl8~pf5w^-ovxgIQN@(g)t<-ACfTEm*AJ=1?VK3PVttj2&C7&6s)vr?SBg_T^`OgB!*!P4^HwCd3Gy9I%?r*dRA zxOelj#>IR2jeAq%E;idstd-@2=F^(4Y44T%Av^J(mgiqXdD3#d24Db}G=e%NLm@8O zc&!*TA~sok%1pm1_fq>|{guGJLy=BqL(MZr8=@5hUvlJwjE7w^k1cI272_2qa-kGM zcy0!uQlVWmpT2znT44HD`cl18%+&rh1m^nw{Tt$|*Pd2zWk_~Q*|r2x1h0e4gi^dh zoVI{wY`K7BkVALP!yNvre*8<-*Ee`Jm=qHYLw;R83*@uTx*>u(-&@_Z^YV%lhx#n` z0ovNc{@w&se~e?HUCTT%3hr?zFaW4YEM4NulTII*JyEPOg@bt7gP`kt1lQTSoHFMlP%ay@+TFpU(pB(Lu7)@dC|7Y;-vrjdRbUFAiYzZ~tJ3 zOsht~s~rsiw@4OE-ZZVwivlDv7C`#cLsvRh0r}m~oyA_4fD#zSYuvgx zA~V?lv}~XmTcJZ5!$J5oNkf9B?VtHPc7K&DM(dazXeeeCJ9a%BNxLr>tr|h;-20fS zY$3^O>p7b2H-JvotebTwd^wzfqO5Cc57lg6nwthuxjqAxMRV6*oX``wDp2udHFr4_ zRp-dkTD^rj_GuOl>j#`CG`%DZaN|ms6!QdV+-H4>VSsnPaiR}wOS#xLvmTrBspus4 z`FHKpe)wXKykJpO|5Xq^OC%4@HKK%&-}m{1aBeL zdF9*C-YvvRX5j6B<~F$gI;ulK(2V+lu)Dl#2`BUUXoiIxXKl%y{LtF#l=c!+dkpgK zfWwq#p5&1+v}4*4oz4XF=9z$Yu$QN8md}Xctv-#-&%5)ua-O%$MSp{)Y?U*zdb3Oi z&D$=%WKs)Ad5Q5!pc%Y9c+ z=v?h$mT*e$Y!&l@X=mFp*_uzti1TVbtN1eL7lShMetj&{JML(Het-HR`A!LIK4@3$ zbL8-K#A^dQtI*?-6x<{$R$mk0O$@w zCxqXt4~BJA!X(MosiG?E1}PY@q%{$@?A)bfVw~MY*3LogHD0vmy4bca+s%==r<(}` z94&-b8J@Uya<@4@>NgTrh_x%qfpE7`?q2G&!~z#4J-W?*~Ll%O^l_`uk$ma0ZJuGfp1L)T&9oUS* zc{3CpyIWCfKL@+m&&-yizogE#<5q+K=7?GPDqA%9b1tkGgzymN4=`W~_V)*r%u|Zb zYsitCYdHno8HzsZIx}$yTc^9JrHiSItq_KKQU=0Sr#9EmIKC~Jghs_Uo-V566nRCdR#uS$jnK&ohgDNfFD=rGP6SyiRRG?e z)1Ik!`8Xb#>;6>x`YK{^AyPbvqhj{$e)9sS!VINN$a@2MD=Jv5tY@U?;j^}yYSihs^a~zp>cJTHp-b?SVN;9tOHmfTp5w^UVB8XLwym5W6f{3zI{3Ek>gsjUw zP>WYc*wlG3M-bjfuHL7y<;~NJqdJ(PFtUW0!S1j`*KN~f_2O)+K()AKgPmplzZkFs zY}vaUy1VE7`yH@J4TQ(FbNKPo)%oZai(UVp7RQCB1K(`CjZC!rzo+eX{JM;K@$god zn^UKFov=dEaCKd7PNhN_U{APZM4Pg!1;xe1e_y?V?zSkcb7h!*dOpu14f`pFeO3Q# ze8lz9{Rz+v+z&aLiW$z+i!q2}H@6cMu67&hjtf5yunc>>`-?~^W0_PWCj7@8l?Pj( zqHhtTvGsmF#+y)gCG^g<9lTv|tW0>^%?}s}^@Dq#gw2*LQyg~s%uNq{+W`$BpSI4r zAgk~*gj^4&JHYlO_5;C*3iY z8Gem)x#j3r%8dBut;(aMPS?K1r(>@Tc5V&`z>)%P>mOB(q?7Aqmdn+ot}M3@{tP(^ zW@Zl^uwQCB1eIkpd&RrB$Lty_;RvwZeA=_xyK=Fp7I9Nf7~=>beL{_ z7OMlb0cSJ?mm&G7LC@*pj}I2(W&{1vIyPUf2GKhY4BSZxIfnZ$raIAdNyFPI-KOui z^!&(=KE8N*yo@FYMs`B}V%INu)WVm}-KgvBwm@`HE`YT0>FGXad*`&!6A(Mik0r1} z<$cm&kThiyqbMRZJYwr*F_@jjdHQ@4#=hgU8zH>)427IQB_b2Z`$XugW&GFBjb0oO z=mK?1|A;s=+QDn^t($vW|hO zKB%5SZdF0^n~#=uJ2-pbydI+5YewGC`JU)QS^uNy<5qz(q5I>gJUa+09&>90;6@SC z?M7sX+N$+sfIADu@By2dSR8Nn``K1?X6@lP>x{M2T`2x2V5LroOb1^H-;G45hp~N$ z!NF=hN|~N{9?hop^2@G}xOjA5+cfCn7_McU);gd3A$P7ttip+3atF@6YjA!-c0Wnn zC&IRJF?!7HuZxX#7{f(3g`&a52xn%}xd-{tJMkVguPkGcy~Bi;b>}GWSlGQL=vY|S z-wIR>k8*i?WMQbr2G16QWx9sm6s|Yaw-+XcQ}2rR3hM zx)em?)TTw^>(rRe}CtosBrP#5Zr=oJ`H@aoF`fx<|Q~o(rW4N}byl!uEzpT7AZ>wnZ zBwYpmIehq~SEIh&IIEubPKyiFh;q4=^9H`pas%^Qy3$2CgVUGsbB(cq+rN4e6RzH~ zDqyv>EAHZ7ol!bFQbc~QbJx=t;e@ZuI}dA_o2E9ExY=OeGP&1!M1_L$S*o6?cot}v zvOth1A5lGU>yB1%O0DMt*(ekA;+OMw!}W4EZP$(KWaqScE<&2Hw zm3($TZ9rN%?W{V`fOr3Zw@#lVB-p?jw%el8e@kHU#l!n%JL5RHke$z*W^^CtFCvBu z;A?Bnr>mJ7;T39#IMiM%>XRa1rVb8pX@ou4AL50_1r)47nD)HdIM-*+@DW`CtQ}1= zF786$B3;&rmZ;Ks-Cl+ zy_epWa@ceXw0&Rh(0%CfUgFiF&7VFYj63fXhcZF{)!uq6cM5t%;y;Asjl%zH_EPoD6;_mbjcwADFs&gqLC3tzE94kE_mx3~U6Yf30e0d{j20a}@ei?VUkLYZhw|rT4R-hQ_<%G^ zoVws#+RT|{!cvFx&gWNir0P}15SOQ~X&YZEiJiPE6ZTPtBgD=!aQTE((J@5;BHHn#QN}l^0OanIJ1tG_nZLxX3Imuwnf! z84-^X_cFh1{`91Fr-{E%Zu8~WH+=N$xb%9m@BBl)lD~QPmi%~+^KHmg*U$&SPX+qJ zp9ws3b8F++uIrf<29#DOD@3iA+mWDm^cN4S=@Z3i513%4t!R0pWJv&5QCA%QfQaE( z=`Bue0w2}7)GEd|hksc=6*i1mz@NaqT)`P=^>entZ}DixXJoCKi1e$TU0H)AlAx(6 z)nxrlyTc1IGtlx--L$Byf?qOgBRqZRpe-Jro^kVup)_x3nQBs>mC@E@Wui#J-91$g z`7WCVoit^>U@vU{%63|HS0*;q#o6?Hg?N5NNy$kidyZ49A-ZIVV-wR6 zNjd{Og^d3Gb>cIeGnP?xz}1}S{a~eDP(%_5?lG$8$A=^dK$C1n2qSviY2~@RWc^;> z*1tVR*Q;^)ZcQkk)v!F5=G_PJ+>k)V+;~B!j2?wXl{?L5I13hh!KT$N>lq#`6U7s5 zA7(ekic7^FyD#J*cg|?yUbm}=`cJQ)dTX$&PO0c6^5pZ(j~z>Pd>F0-q9eKItqH#c+1WShRc*cPDC zF?_}E&NNwM5|nf^g{nq{g%`Ztg5b^Ff=U90dyz6~9PF6{46G^4r$>NH?RSdF zo+>yW84DEDkZAL-Je#aGFCgt67o}e=)^c)kfT)?qVEv}rT^#MrYlls%2{3R8 z6Y1AU4dvnw4B+!t0m%=ecigBO^8t;iLN%y&ATdZa2AA{(fh2Ud$R*1zbYAluUG;SX;=)->x_vg+yG+1ZJ6ldM&a|3V*7;I+wj zIFV7&_A1yqO2E>a_>~AkRQ>RU;Z{PW=exCs*;e|G?dld>MZ3-&B9i8euH_Qkz8knl zZtQMwYeeFHY@nN(`@M`0mu|fO?>}$KvA@=^lXEPV{dUf$7u-S+zvCl>1HP1R->i+$a&>O(VinYjG(|{OY*@Z zcUsBdGH`3oPmoEqu~ny?wgUHrsn+IY%O!E%K-3+<#gZ-63VOo>ewAgH-)LN*5^{ew zYx>-y?6HTt_33`G(qbTmZEucL^IpT*p^-2hu5Ue7lKHGC!IcA7I=QI&?U5Oup@}2Y zj3+_v!gbjhu?tkeT_q&`kjWa4V#{_5 zTxl5O^o#e-BAMc~a(KwN(2Csy+{JjDLdUcUfZY<2`+ zu{^;eNSXDXEOUvUAPA#5LTQed+2l}ZSk@9$iJVOq_tzOfqtcWJTT3Nda9un9K3#AJazM1!u>VN-3MlFxs8MmjQ` zAiNqxB-k2`&AeOAv+3#w|g!O%$SM^iTRAWrJ_@`PO- zhda;k4j;a9hHw+_r}Am4sZt``i*3H}GymO+kcG=5kksJYZQ(rbeW4K%!_5IEZ4pC9 ztnI`cx|$?Tgm3S)1be3NC5;t11x;TmZnF_>;`1{X7r8R4b!|j%`{ne${8Jk;WojA| zN>NwUvllH7D9duvL{M}y13Ts;fwDna_G_QlsYT`lxnGo7EHEm!v6m6`L>B_a`p?`i z#U&oz+x}K}@^8V(w^rE^Hq6^$!A^JEn3MW))N+>8ct_1Ef*x3nmwfy5S|{B}_J08f z06n;3=SQZEN8uFjBPR+%X&Q5_HX`2D`x0!j`lqU0Ny^~((QhiZ_>Ku?FB^D7%jytQ zL~xCZj?3S^APS8Vti8k`8&6N+S{YQElboa*J{;--)dl#1_Q+g`}` zp5n;4omGNsDtKnwE?33ke=Ulss{6}z?n}6&+z?GL`DVqmLmL&(uMs*$qiWD|lY6k< zaGl!uj_p@boKV^p zlOWTlI7Xl@GQ7hrXGC0fV?OhOrW&^;;a-vEo7p)~q3w6H?;d}L*GIEq1-(V>YwuHP z2i2>%+Si-|VsbumJ8PN|lHz~?L42in*Et5d1O-Q885Q@@ifPrnE}Hec>h-+o%EUG!q%}ngCbZ=FRDgO>R%16lWG?i-`Latlo}>w2 zGu=*-x6|t`L5lj&@}048Gb?ASmmaQN?+GxA=k$uJ$1MQ{jt6snMfW3Oh|tBCR@GNVZ5>&BT#j>Ae`{Yj4DlXRpZN^MQ=) zhZ{spKLViw&p>@Ean1P6;GuB96Z``nTx&?VlQE|H$p?8Rcq`204JkeEeR=MYi6Tgl;LrX~bhr)Ggw0aYAEL4o zST#5lDl($ORe~i3+G*2{K>@->BNAy$O8S)xn@s%nd0!c|h+|v}cUiyyt;yhuvSOp8 z^W|mZwg@U4uVOodSmyh056C%|&jHr`)xlX;vMJn6R$NE`ZUGWMT}l1E4GZ`V5NkoB z+C7c_Y9+hXsp|3$Zu}nqt!C+oaytS3)UJhd-|GW)jHvscGUcP|$S&9;#V;zz0|Fid zo8}I5&pvM_!~4ZQnnz|F&#aR9=mwrLDShIw&Bj$G^@LoJZ(sBF-I0&$Hg8fdgkjzD z?^M&LCfpraseO5)zBk%|xrR~i-qgwsLQ}dlo_mt>rzT6D(wu=yhqTjvUU-#0&wu+L*Z%X@Q9mhXSmo}np{$dIBt=%E#rq&_w zWtk%tv##Iyc9Y&?a<8e&mc{$i^!?)ZDm`3XM52OQAt)7m1z)UH;v$$U}BLN&)475g|nz%s<%%j|KGwm>r>yLQ=&P!1LM9%eTy z!5HT#E~gA+(bx90AwIj0E)^=lG8++B6C&HKqSo9h685$W-w`aRM>ELWmNQDJ%pE$h z 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�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

    -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]

    Registers a new Function listener. @@ -2207,6 +2264,7 @@

    Methods

    App

    • action
    • +
    • assistant
    • attachment_action
    • block_action
    • block_suggestion
    • diff --git a/docs/static/api-docs/slack_bolt/app/async_app.html b/docs/static/api-docs/slack_bolt/app/async_app.html index b5c7251da..837f4befe 100644 --- a/docs/static/api-docs/slack_bolt/app/async_app.html +++ b/docs/static/api-docs/slack_bolt/app/async_app.html @@ -37,7 +37,7 @@

      Classes

      class AsyncApp -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, before_authorize: Union[AsyncMiddleware, Callable[..., Awaitable[Any]], ForwardRef(None)] = None, authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[AsyncOAuthSettings] = None, oauth_flow: Optional[AsyncOAuthFlow] = None, verification_token: Optional[str] = None) +(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, before_authorize: Union[AsyncMiddleware, Callable[..., Awaitable[Any]], ForwardRef(None)] = None, authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[AsyncOAuthSettings] = None, oauth_flow: Optional[AsyncOAuthFlow] = None, verification_token: Optional[str] = None, assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None)

      Bolt App that provides functionalities to register middleware/listeners.

      @@ -105,6 +105,10 @@

      Args

      False if you would like to disable the built-in middleware (Default: True). AsyncIgnoringSelfEvents is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
      +
      ignoring_self_assistant_message_events_enabled
      +
      False if you would like to disable the built-in middleware. +IgnoringSelfEvents for this app's bot user message events within an assistant thread +This is useful for avoiding code error causing an infinite loop; Default: True
      url_verification_enabled
      False if you would like to disable the built-in middleware (Default: True). AsyncUrlVerification is a built-in middleware that handles url_verification requests @@ -121,7 +125,10 @@

      Args

      oauth_flow
      Instantiated slack_bolt.oauth.AsyncOAuthFlow. This is always prioritized over oauth_settings.
      verification_token
      -
      Deprecated verification mechanism. This can used only for ssl_check requests.
      +
      Deprecated verification mechanism. This can be used only for ssl_check requests.
      +
      assistant_thread_context_store
      +
      Custom AssistantThreadContext store (Default: the built-in implementation, +which uses a parent message's metadata to store the latest context)
    @@ -153,6 +160,7 @@

    Args

    # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -161,6 +169,8 @@

    Args

    oauth_flow: Optional[AsyncOAuthFlow] = None, # No need to set (the value is used only in response to ssl_check requests) verification_token: Optional[str] = None, + # for AI Agents & Assistants + assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -215,6 +225,9 @@

    Args

    ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `AsyncIgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). + ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. + `IgnoringSelfEvents` for this app's bot user message events within an assistant thread + This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `AsyncUrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. @@ -225,7 +238,9 @@

    Args

    when your app receives `function_executed` or interactivity events scoped to a custom step. oauth_settings: The settings related to Slack app installation flow (OAuth flow) oauth_flow: Instantiated `slack_bolt.oauth.AsyncOAuthFlow`. This is always prioritized over oauth_settings. - verification_token: Deprecated verification mechanism. This can used only for ssl_check requests. + verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. + assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, + which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") @@ -375,6 +390,8 @@

    Args

    self._async_middleware_list: List[AsyncMiddleware] = [] self._async_listeners: List[AsyncListener] = [] + self._assistant_thread_context_store = assistant_thread_context_store + self._process_before_response = process_before_response self._async_listener_runner = AsyncioListenerRunner( logger=self._framework_logger, @@ -394,6 +411,7 @@

    Args

    self._init_async_middleware_list( request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, @@ -406,6 +424,7 @@

    Args

    self, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -458,7 +477,12 @@

    Args

    raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: - self._async_middleware_list.append(AsyncIgnoringSelfEvents(base_logger=self._base_logger)) + self._async_middleware_list.append( + AsyncIgnoringSelfEvents( + base_logger=self._base_logger, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, + ) + ) if url_verification_enabled is True: self._async_middleware_list.append(AsyncUrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: @@ -711,6 +735,8 @@

    Args

    if isinstance(middleware_or_callable, AsyncMiddleware): middleware: AsyncMiddleware = middleware_or_callable self._async_middleware_list.append(middleware) + if isinstance(middleware, AsyncAssistant) and middleware.thread_context_store is not None: + self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._async_middleware_list.append( AsyncCustomMiddleware( @@ -724,6 +750,9 @@

    Args

    raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None + def assistant(self, assistant: AsyncAssistant) -> Optional[Callable]: + return self.middleware(assistant) + # ------------------------- # Workflows: Steps from apps @@ -789,7 +818,7 @@

    Args

    elif not isinstance(step, AsyncWorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") - self.use(AsyncWorkflowStepMiddleware(step, self._async_listener_runner)) + self.use(AsyncWorkflowStepMiddleware(step)) # ------------------------- # global error handler @@ -939,6 +968,7 @@

    Args

    callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, + auto_acknowledge: bool = True, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -975,7 +1005,7 @@

    Args

    primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, True) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) return __call__ @@ -1418,6 +1448,24 @@

    Args

    ) req.context["client"] = client_per_request + # Most apps do not need this "listener_runner" instance. + # It is intended for apps that start lazy listeners from their custom global middleware. + req.context["listener_runner"] = self.listener_runner + + # For AI Agents & Assistants + if is_assistant_event(req.body): + assistant = AsyncAssistantUtilities( + payload=to_event(req.body), # type:ignore[arg-type] + context=req.context, + thread_context_store=self._assistant_thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + @staticmethod def _to_listener_functions( kwargs: dict, @@ -1610,6 +1658,12 @@

    Args

    Only when all the middleware call next() method, the listener function can be invoked.
    +
    +def assistant(self, assistant: AsyncAssistant) ‑> Optional[Callable] +
    +
    +
    +
    async def async_dispatch(self, req: AsyncBoltRequest) ‑> BoltResponse
    @@ -1766,7 +1820,7 @@

    Args

    -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]] +def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]

    Registers a new Function listener. @@ -2115,6 +2169,7 @@

  • AsyncSlackAppServer
  • action
  • +
  • assistant
  • async_dispatch
  • attachment_action
  • block_action
  • diff --git a/docs/static/api-docs/slack_bolt/app/index.html b/docs/static/api-docs/slack_bolt/app/index.html index bd6a2b599..b43977c61 100644 --- a/docs/static/api-docs/slack_bolt/app/index.html +++ b/docs/static/api-docs/slack_bolt/app/index.html @@ -56,7 +56,7 @@

    Classes

    class App -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None) +(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None, assistant_thread_context_store: Optional[AssistantThreadContextStore] = None)

    Bolt App that provides functionalities to register middleware/listeners.

    @@ -126,6 +126,10 @@

    Args

    False if you would like to disable the built-in middleware (Default: True). IgnoringSelfEvents is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
    +
    ignoring_self_assistant_message_events_enabled
    +
    False if you would like to disable the built-in middleware. +IgnoringSelfEvents for this app's bot user message events within an assistant thread +This is useful for avoiding code error causing an infinite loop; Default: True
    url_verification_enabled
    False if you would like to disable the built-in middleware (Default: True). UrlVerification is a built-in middleware that handles url_verification requests @@ -146,6 +150,9 @@

    Args

    listener_executor
    Custom executor to run background tasks. If absent, the default ThreadPoolExecutor will be used.
    +
    assistant_thread_context_store
    +
    Custom AssistantThreadContext store (Default: the built-in implementation, +which uses a parent message's metadata to store the latest context)

    @@ -178,6 +185,7 @@

    Args

    # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -188,6 +196,8 @@

    Args

    verification_token: Optional[str] = None, # Set this one only when you want to customize the executor listener_executor: Optional[Executor] = None, + # for AI Agents & Assistants + assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -243,6 +253,9 @@

    Args

    ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). + ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. + `IgnoringSelfEvents` for this app's bot user message events within an assistant thread + This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. @@ -256,6 +269,8 @@

    Args

    verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. + assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, + which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") @@ -402,6 +417,8 @@

    Args

    if listener_executor is None: listener_executor = ThreadPoolExecutor(max_workers=5) + self._assistant_thread_context_store = assistant_thread_context_store + self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( logger=self._framework_logger, @@ -424,6 +441,7 @@

    Args

    token_verification_enabled=token_verification_enabled, request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, @@ -435,6 +453,7 @@

    Args

    token_verification_enabled: bool = True, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -495,7 +514,12 @@

    Args

    raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: - self._middleware_list.append(IgnoringSelfEvents(base_logger=self._base_logger)) + self._middleware_list.append( + IgnoringSelfEvents( + base_logger=self._base_logger, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, + ) + ) if url_verification_enabled is True: self._middleware_list.append(UrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: @@ -720,6 +744,8 @@

    Args

    if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) + if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: + self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( @@ -733,6 +759,12 @@

    Args

    raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None + # ------------------------- + # AI Agents & Assistants + + def assistant(self, assistant: Assistant) -> Optional[Callable]: + return self.middleware(assistant) + # ------------------------- # Workflows: Steps from apps @@ -799,7 +831,7 @@

    Args

    elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") - self.use(WorkflowStepMiddleware(step, self.listener_runner)) + self.use(WorkflowStepMiddleware(step)) # ------------------------- # global error handler @@ -941,6 +973,7 @@

    Args

    callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, + auto_acknowledge: bool = True, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -975,7 +1008,7 @@

    Args

    def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, True) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) return __call__ @@ -1414,6 +1447,24 @@

    Args

    ) req.context["client"] = client_per_request + # Most apps do not need this "listener_runner" instance. + # It is intended for apps that start lazy listeners from their custom global middleware. + req.context["listener_runner"] = self.listener_runner + + # For AI Agents & Assistants + if is_assistant_event(req.body): + assistant = AssistantUtilities( + payload=to_event(req.body), # type:ignore[arg-type] + context=req.context, + thread_context_store=self._assistant_thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + @staticmethod def _to_listener_functions( kwargs: dict, @@ -1593,6 +1644,12 @@

    Args

    Only when all the middleware call next() method, the listener function can be invoked.
    +
    +def assistant(self, assistant: Assistant) ‑> Optional[Callable] +
    +
    +
    +
    def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]
    @@ -1749,7 +1806,7 @@

    Args

    -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]

    Registers a new Function listener. @@ -2069,6 +2126,7 @@

    Args

    App

    • action
    • +
    • assistant
    • attachment_action
    • block_action
    • block_suggestion
    • diff --git a/docs/static/api-docs/slack_bolt/async_app.html b/docs/static/api-docs/slack_bolt/async_app.html index 39aaed50c..13d767100 100644 --- a/docs/static/api-docs/slack_bolt/async_app.html +++ b/docs/static/api-docs/slack_bolt/async_app.html @@ -128,7 +128,7 @@

      Class variables

    class AsyncApp -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, before_authorize: Union[AsyncMiddleware, Callable[..., Awaitable[Any]], ForwardRef(None)] = None, authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[AsyncOAuthSettings] = None, oauth_flow: Optional[AsyncOAuthFlow] = None, verification_token: Optional[str] = None) +(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, before_authorize: Union[AsyncMiddleware, Callable[..., Awaitable[Any]], ForwardRef(None)] = None, authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[AsyncOAuthSettings] = None, oauth_flow: Optional[AsyncOAuthFlow] = None, verification_token: Optional[str] = None, assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None)

    Bolt App that provides functionalities to register middleware/listeners.

    @@ -196,6 +196,10 @@

    Args

    False if you would like to disable the built-in middleware (Default: True). AsyncIgnoringSelfEvents is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
    +
    ignoring_self_assistant_message_events_enabled
    +
    False if you would like to disable the built-in middleware. +IgnoringSelfEvents for this app's bot user message events within an assistant thread +This is useful for avoiding code error causing an infinite loop; Default: True
    url_verification_enabled
    False if you would like to disable the built-in middleware (Default: True). AsyncUrlVerification is a built-in middleware that handles url_verification requests @@ -212,7 +216,10 @@

    Args

    oauth_flow
    Instantiated slack_bolt.oauth.AsyncOAuthFlow. This is always prioritized over oauth_settings.
    verification_token
    -
    Deprecated verification mechanism. This can used only for ssl_check requests.
    +
    Deprecated verification mechanism. This can be used only for ssl_check requests.
    +
    assistant_thread_context_store
    +
    Custom AssistantThreadContext store (Default: the built-in implementation, +which uses a parent message's metadata to store the latest context)
    @@ -244,6 +251,7 @@

    Args

    # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -252,6 +260,8 @@

    Args

    oauth_flow: Optional[AsyncOAuthFlow] = None, # No need to set (the value is used only in response to ssl_check requests) verification_token: Optional[str] = None, + # for AI Agents & Assistants + assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -306,6 +316,9 @@

    Args

    ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `AsyncIgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). + ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. + `IgnoringSelfEvents` for this app's bot user message events within an assistant thread + This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `AsyncUrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. @@ -316,7 +329,9 @@

    Args

    when your app receives `function_executed` or interactivity events scoped to a custom step. oauth_settings: The settings related to Slack app installation flow (OAuth flow) oauth_flow: Instantiated `slack_bolt.oauth.AsyncOAuthFlow`. This is always prioritized over oauth_settings. - verification_token: Deprecated verification mechanism. This can used only for ssl_check requests. + verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. + assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, + which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") @@ -466,6 +481,8 @@

    Args

    self._async_middleware_list: List[AsyncMiddleware] = [] self._async_listeners: List[AsyncListener] = [] + self._assistant_thread_context_store = assistant_thread_context_store + self._process_before_response = process_before_response self._async_listener_runner = AsyncioListenerRunner( logger=self._framework_logger, @@ -485,6 +502,7 @@

    Args

    self._init_async_middleware_list( request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, @@ -497,6 +515,7 @@

    Args

    self, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -549,7 +568,12 @@

    Args

    raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: - self._async_middleware_list.append(AsyncIgnoringSelfEvents(base_logger=self._base_logger)) + self._async_middleware_list.append( + AsyncIgnoringSelfEvents( + base_logger=self._base_logger, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, + ) + ) if url_verification_enabled is True: self._async_middleware_list.append(AsyncUrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: @@ -802,6 +826,8 @@

    Args

    if isinstance(middleware_or_callable, AsyncMiddleware): middleware: AsyncMiddleware = middleware_or_callable self._async_middleware_list.append(middleware) + if isinstance(middleware, AsyncAssistant) and middleware.thread_context_store is not None: + self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._async_middleware_list.append( AsyncCustomMiddleware( @@ -815,6 +841,9 @@

    Args

    raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None + def assistant(self, assistant: AsyncAssistant) -> Optional[Callable]: + return self.middleware(assistant) + # ------------------------- # Workflows: Steps from apps @@ -880,7 +909,7 @@

    Args

    elif not isinstance(step, AsyncWorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") - self.use(AsyncWorkflowStepMiddleware(step, self._async_listener_runner)) + self.use(AsyncWorkflowStepMiddleware(step)) # ------------------------- # global error handler @@ -1030,6 +1059,7 @@

    Args

    callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, + auto_acknowledge: bool = True, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -1066,7 +1096,7 @@

    Args

    primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, True) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) return __call__ @@ -1509,6 +1539,24 @@

    Args

    ) req.context["client"] = client_per_request + # Most apps do not need this "listener_runner" instance. + # It is intended for apps that start lazy listeners from their custom global middleware. + req.context["listener_runner"] = self.listener_runner + + # For AI Agents & Assistants + if is_assistant_event(req.body): + assistant = AsyncAssistantUtilities( + payload=to_event(req.body), # type:ignore[arg-type] + context=req.context, + thread_context_store=self._assistant_thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + @staticmethod def _to_listener_functions( kwargs: dict, @@ -1701,6 +1749,12 @@

    Args

    Only when all the middleware call next() method, the listener function can be invoked. +
    +def assistant(self, assistant: AsyncAssistant) ‑> Optional[Callable] +
    +
    +
    +
    async def async_dispatch(self, req: AsyncBoltRequest) ‑> BoltResponse
    @@ -1857,7 +1911,7 @@

    Args

    -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]] +def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]

    Registers a new Function listener. @@ -2186,6 +2240,378 @@

    Args

    +
    +class AsyncAssistant +(*, app_name: str = 'assistant', thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +
    +
    +

    A middleware can process request data before other middleware and listener functions.

    +
    + +Expand source code + +
    class AsyncAssistant(AsyncMiddleware):
    +    _thread_started_listeners: Optional[List[AsyncListener]]
    +    _user_message_listeners: Optional[List[AsyncListener]]
    +    _bot_message_listeners: Optional[List[AsyncListener]]
    +    _thread_context_changed_listeners: Optional[List[AsyncListener]]
    +
    +    thread_context_store: Optional[AsyncAssistantThreadContextStore]
    +    base_logger: Optional[logging.Logger]
    +
    +    def __init__(
    +        self,
    +        *,
    +        app_name: str = "assistant",
    +        thread_context_store: Optional[AsyncAssistantThreadContextStore] = None,
    +        logger: Optional[logging.Logger] = None,
    +    ):
    +        self.app_name = app_name
    +        self.thread_context_store = thread_context_store
    +        self.base_logger = logger
    +
    +        self._thread_started_listeners = None
    +        self._thread_context_changed_listeners = None
    +        self._user_message_listeners = None
    +        self._bot_message_listeners = None
    +
    +    def thread_started(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._thread_started_listeners is None:
    +            self._thread_started_listeners = []
    +        all_matchers = self._merge_matchers(
    +            build_listener_matcher(
    +                func=is_assistant_thread_started_event,
    +                asyncio=True,
    +                base_logger=self.base_logger,
    +            ),  # type:ignore[arg-type]
    +            matchers,
    +        )
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._thread_started_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._thread_started_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    def user_message(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._user_message_listeners is None:
    +            self._user_message_listeners = []
    +        all_matchers = self._merge_matchers(
    +            build_listener_matcher(
    +                func=is_user_message_event_in_assistant_thread,
    +                asyncio=True,
    +                base_logger=self.base_logger,
    +            ),  # type:ignore[arg-type]
    +            matchers,
    +        )
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._user_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._user_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    def bot_message(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._bot_message_listeners is None:
    +            self._bot_message_listeners = []
    +        all_matchers = self._merge_matchers(
    +            build_listener_matcher(
    +                func=is_bot_message_event_in_assistant_thread,
    +                asyncio=True,
    +                base_logger=self.base_logger,
    +            ),  # type:ignore[arg-type]
    +            matchers,
    +        )
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._bot_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._bot_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    def thread_context_changed(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._thread_context_changed_listeners is None:
    +            self._thread_context_changed_listeners = []
    +        all_matchers = self._merge_matchers(
    +            build_listener_matcher(
    +                func=is_assistant_thread_context_changed_event,
    +                asyncio=True,
    +                base_logger=self.base_logger,
    +            ),  # type:ignore[arg-type]
    +            matchers,
    +        )
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._thread_context_changed_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._thread_context_changed_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    @staticmethod
    +    def _merge_matchers(
    +        primary_matcher: Union[Callable[..., bool], AsyncListenerMatcher],
    +        custom_matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]],
    +    ):
    +        return [primary_matcher] + (custom_matchers or [])  # type:ignore[operator]
    +
    +    @staticmethod
    +    async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict):
    +        new_context: dict = payload["assistant_thread"]["context"]
    +        await save_thread_context(new_context)
    +
    +    async def async_process(  # type:ignore[return]
    +        self,
    +        *,
    +        req: AsyncBoltRequest,
    +        resp: BoltResponse,
    +        next: Callable[[], Awaitable[BoltResponse]],
    +    ) -> Optional[BoltResponse]:
    +        if self._thread_context_changed_listeners is None:
    +            self.thread_context_changed(self.default_thread_context_changed)
    +
    +        listener_runner: AsyncioListenerRunner = req.context.listener_runner
    +        for listeners in [
    +            self._thread_started_listeners,
    +            self._thread_context_changed_listeners,
    +            self._user_message_listeners,
    +            self._bot_message_listeners,
    +        ]:
    +            if listeners is not None:
    +                for listener in listeners:
    +                    if listener is not None and await listener.async_matches(req=req, resp=resp):
    +                        return await listener_runner.run(
    +                            request=req,
    +                            response=resp,
    +                            listener_name="assistant_listener",
    +                            listener=listener,
    +                        )
    +        if is_other_message_sub_event_in_assistant_thread(req.body):
    +            # message_changed, message_deleted, etc.
    +            return await req.context.ack()
    +
    +        await next()
    +
    +    def build_listener(
    +        self,
    +        listener_or_functions: Union[AsyncListener, Callable, List[Callable]],
    +        matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]] = None,
    +        middleware: Optional[List[AsyncMiddleware]] = None,
    +        base_logger: Optional[Logger] = None,
    +    ) -> AsyncListener:
    +        if isinstance(listener_or_functions, Callable):  # type:ignore[arg-type]
    +            listener_or_functions = [listener_or_functions]  # type:ignore[list-item]
    +
    +        if isinstance(listener_or_functions, AsyncListener):
    +            return listener_or_functions
    +        elif isinstance(listener_or_functions, list):
    +            middleware = middleware if middleware else []
    +            functions = listener_or_functions
    +            ack_function = functions.pop(0)
    +
    +            matchers = matchers if matchers else []
    +            listener_matchers: List[AsyncListenerMatcher] = []
    +            for matcher in matchers:
    +                if isinstance(matcher, AsyncListenerMatcher):
    +                    listener_matchers.append(matcher)
    +                else:
    +                    listener_matchers.append(
    +                        build_listener_matcher(
    +                            func=matcher,  # type:ignore[arg-type]
    +                            asyncio=True,
    +                            base_logger=base_logger,
    +                        )
    +                    )
    +            return AsyncCustomListener(
    +                app_name=self.app_name,
    +                matchers=listener_matchers,
    +                middleware=middleware,
    +                ack_function=ack_function,
    +                lazy_functions=functions,
    +                auto_acknowledgement=True,
    +                base_logger=base_logger or self.base_logger,
    +            )
    +        else:
    +            raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected")
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var base_logger : Optional[logging.Logger]
    +
    +
    +
    +
    var thread_context_store : Optional[AsyncAssistantThreadContextStore]
    +
    +
    +
    +
    +

    Static methods

    +
    +
    +async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict) +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def bot_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +def build_listener(self, listener_or_functions: Union[AsyncListener, Callable, List[Callable]], matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]] = None, middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> AsyncListener +
    +
    +
    +
    +
    +def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +def thread_started(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +def user_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +

    Inherited members

    + +
    class AsyncBoltContext (*args, **kwargs) @@ -2202,22 +2628,31 @@

    Args

    def to_copyable(self) -> "AsyncBoltContext": new_dict = {} for prop_name, prop_value in self.items(): - if prop_name in self.standard_property_names: + if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value + elif prop_name in self.non_copyable_standard_property_names: + # Do nothing with this property (e.g., listener_runner) + continue else: try: copied_value = create_copy(prop_value) new_dict[prop_name] = copied_value except TypeError as te: self.logger.debug( - f"Skipped settings '{prop_name}' to a copied request for lazy listeners " + f"Skipped setting '{prop_name}' to a copied request for lazy listeners " f"as it's not possible to make a deep copy (error: {te})" ) return AsyncBoltContext(new_dict) + # The return type is intentionally string to avoid circular imports @property - def client(self) -> Optional[AsyncWebClient]: + def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defined] + """The properly configured listener_runner that is available for middleware/listeners.""" + return self["listener_runner"] + + @property + def client(self) -> AsyncWebClient: """The `AsyncWebClient` instance available for this request. @app.event("app_mention") @@ -2281,7 +2716,7 @@

    Args

    Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"] @property @@ -2305,8 +2740,8 @@

    Args

    if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"] @@ -2331,9 +2766,7 @@

    Args

    Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property @@ -2357,10 +2790,28 @@

    Args

    Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) - return self["fail"]
    + self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) + return self["fail"] + + @property + def set_title(self) -> Optional[AsyncSetTitle]: + return self.get("set_title") + + @property + def set_status(self) -> Optional[AsyncSetStatus]: + return self.get("set_status") + + @property + def set_suggested_prompts(self) -> Optional[AsyncSetSuggestedPrompts]: + return self.get("set_suggested_prompts") + + @property + def get_thread_context(self) -> Optional[AsyncGetThreadContext]: + return self.get("get_thread_context") + + @property + def save_thread_context(self) -> Optional[AsyncSaveThreadContext]: + return self.get("save_thread_context")

    Ancestors

      @@ -2408,7 +2859,7 @@

      Returns

      return self["ack"]
      -
      prop client : Optional[slack_sdk.web.async_client.AsyncWebClient]
      +
      prop client : slack_sdk.web.async_client.AsyncWebClient

      The AsyncWebClient instance available for this request.

      @app.event("app_mention")
      @@ -2433,7 +2884,7 @@ 

      Returns

      Expand source code
      @property
      -def client(self) -> Optional[AsyncWebClient]:
      +def client(self) -> AsyncWebClient:
           """The `AsyncWebClient` instance available for this request.
       
               @app.event("app_mention")
      @@ -2502,9 +2953,7 @@ 

      Returns

      Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"]
      @@ -2551,12 +3000,35 @@

      Returns

      Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"]
      +
      prop get_thread_context : Optional[AsyncGetThreadContext]
      +
      +
      +
      + +Expand source code + +
      @property
      +def get_thread_context(self) -> Optional[AsyncGetThreadContext]:
      +    return self.get("get_thread_context")
      +
      +
      +
      prop listener_runner : AsyncioListenerRunner
      +
      +

      The properly configured listener_runner that is available for middleware/listeners.

      +
      + +Expand source code + +
      @property
      +def listener_runner(self) -> "AsyncioListenerRunner":  # type: ignore[name-defined]
      +    """The properly configured listener_runner that is available for middleware/listeners."""
      +    return self["listener_runner"]
      +
      +
      prop respond : Optional[AsyncRespond]

      respond() function for this request.

      @@ -2598,12 +3070,24 @@

      Returns

      if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"]
      +
      prop save_thread_context : Optional[AsyncSaveThreadContext]
      +
      +
      +
      + +Expand source code + +
      @property
      +def save_thread_context(self) -> Optional[AsyncSaveThreadContext]:
      +    return self.get("save_thread_context")
      +
      +
      prop sayAsyncSay

      say() function for this request.

      @@ -2643,10 +3127,46 @@

      Returns

      Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"]
      +
      prop set_status : Optional[AsyncSetStatus]
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_status(self) -> Optional[AsyncSetStatus]:
      +    return self.get("set_status")
      +
      +
      +
      prop set_suggested_prompts : Optional[AsyncSetSuggestedPrompts]
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_suggested_prompts(self) -> Optional[AsyncSetSuggestedPrompts]:
      +    return self.get("set_suggested_prompts")
      +
      +
      +
      prop set_title : Optional[AsyncSetTitle]
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_title(self) -> Optional[AsyncSetTitle]:
      +    return self.get("set_title")
      +
      +

      Methods

      @@ -2678,6 +3198,7 @@

      Inherited members

    • matches
    • response_url
    • team_id
    • +
    • thread_ts
    • token
    • user_id
    • user_token
    • @@ -2892,6 +3413,83 @@

      Inherited members

    +
    +class AsyncGetThreadContext +(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +
    +
    +
    +
    + +Expand source code + +
    class AsyncGetThreadContext:
    +    thread_context_store: AsyncAssistantThreadContextStore
    +    payload: dict
    +    channel_id: str
    +    thread_ts: str
    +
    +    _thread_context: Optional[AssistantThreadContext]
    +    thread_context_loaded: bool
    +
    +    def __init__(
    +        self,
    +        thread_context_store: AsyncAssistantThreadContextStore,
    +        channel_id: str,
    +        thread_ts: str,
    +        payload: dict,
    +    ):
    +        self.thread_context_store = thread_context_store
    +        self.payload = payload
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +        self._thread_context: Optional[AssistantThreadContext] = None
    +        self.thread_context_loaded = False
    +
    +    async def __call__(self) -> Optional[AssistantThreadContext]:
    +        if self.thread_context_loaded is True:
    +            return self._thread_context
    +
    +        if self.payload.get("assistant_thread") is not None:
    +            # assistant_thread_started
    +            thread = self.payload["assistant_thread"]
    +            self._thread_context = (
    +                AssistantThreadContext(thread["context"])
    +                if thread.get("context", {}).get("channel_id") is not None
    +                else None
    +            )
    +            # for this event, the context will never be changed
    +            self.thread_context_loaded = True
    +        elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None:
    +            # message event
    +            self._thread_context = await self.thread_context_store.find(channel_id=self.channel_id, thread_ts=self.thread_ts)
    +
    +        return self._thread_context
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var payload : dict
    +
    +
    +
    +
    var thread_context_loaded : bool
    +
    +
    +
    +
    var thread_context_storeAsyncAssistantThreadContextStore
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    class AsyncListener
    @@ -3113,9 +3711,57 @@

    Class variables

    +
    +class AsyncSaveThreadContext +(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class AsyncSaveThreadContext:
    +    thread_context_store: AsyncAssistantThreadContextStore
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        thread_context_store: AsyncAssistantThreadContextStore,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.thread_context_store = thread_context_store
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(self, new_context: Dict[str, str]) -> None:
    +        await self.thread_context_store.save(
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +            context=new_context,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var thread_context_storeAsyncAssistantThreadContextStore
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    class AsyncSay -(client: Optional[slack_sdk.web.async_client.AsyncWebClient], channel: Optional[str]) +(client: Optional[slack_sdk.web.async_client.AsyncWebClient], channel: Optional[str], thread_ts: Optional[str] = None, build_metadata: Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]] = None)
    @@ -3126,14 +3772,20 @@

    Class variables

    class AsyncSay:
         client: Optional[AsyncWebClient]
         channel: Optional[str]
    +    thread_ts: Optional[str]
    +    build_metadata: Optional[Callable[[], Awaitable[Union[Dict, Metadata]]]]
     
         def __init__(
             self,
             client: Optional[AsyncWebClient],
             channel: Optional[str],
    +        thread_ts: Optional[str] = None,
    +        build_metadata: Optional[Callable[[], Awaitable[Union[Dict, Metadata]]]] = None,
         ):
             self.client = client
             self.channel = channel
    +        self.thread_ts = thread_ts
    +        self.build_metadata = build_metadata
     
         async def __call__(
             self,
    @@ -3156,6 +3808,8 @@ 

    Class variables

    **kwargs, ) -> AsyncSlackResponse: if _can_say(self, channel): + if metadata is None and self.build_metadata is not None: + metadata = await self.build_metadata() text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response @@ -3165,7 +3819,7 @@

    Class variables

    blocks=blocks, attachments=attachments, as_user=as_user, - thread_ts=thread_ts, + thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, @@ -3182,6 +3836,10 @@

    Class variables

    message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel + if "thread_ts" not in message: + message["thread_ts"] = thread_ts or self.thread_ts + if "metadata" not in message: + message["metadata"] = metadata return await self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -3190,6 +3848,10 @@

    Class variables

    Class variables

    +
    var build_metadata : Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]]
    +
    +
    +
    var channel : Optional[str]
    @@ -3198,6 +3860,161 @@

    Class variables

    +
    var thread_ts : Optional[str]
    +
    +
    +
    +
    +
    +
    +class AsyncSetStatus +(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class AsyncSetStatus:
    +    client: AsyncWebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: AsyncWebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(self, status: str) -> AsyncSlackResponse:
    +        return await self.client.assistant_threads_setStatus(
    +            status=status,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +class AsyncSetSuggestedPrompts +(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class AsyncSetSuggestedPrompts:
    +    client: AsyncWebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: AsyncWebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> AsyncSlackResponse:
    +        prompts_arg: List[Dict[str, str]] = []
    +        for prompt in prompts:
    +            if isinstance(prompt, str):
    +                prompts_arg.append({"title": prompt, "message": prompt})
    +            else:
    +                prompts_arg.append(prompt)
    +
    +        return await self.client.assistant_threads_setSuggestedPrompts(
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +            prompts=prompts_arg,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +class AsyncSetTitle +(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class AsyncSetTitle:
    +    client: AsyncWebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: AsyncWebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(self, title: str) -> AsyncSlackResponse:
    +        return await self.client.assistant_threads_setTitle(
    +            title=title,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    @@ -3228,6 +4045,7 @@

  • AsyncSlackAppServer
  • action
  • +
  • assistant
  • async_dispatch
  • attachment_action
  • block_action
  • @@ -3266,14 +4084,33 @@

    AsyncAssistant

    + + +
  • AsyncBoltContext

    -
      + @@ -3302,6 +4139,16 @@

      AsyncGetThreadContext

      + + +
    • AsyncListener

      diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize.html b/docs/static/api-docs/slack_bolt/authorization/async_authorize.html index 4a9b022b7..392225e1f 100644 --- a/docs/static/api-docs/slack_bolt/authorization/async_authorize.html +++ b/docs/static/api-docs/slack_bolt/authorization/async_authorize.html @@ -392,10 +392,10 @@

      Ancestors

      return self.authorize_result_cache[token] try: - auth_test_api_response = await context.client.auth_test(token=token) # type: ignore[union-attr] + auth_test_api_response = await context.client.auth_test(token=token) user_auth_test_response = None if user_token is not None and token != user_token: - user_auth_test_response = await context.client.auth_test(token=user_token) # type: ignore[union-attr] + user_auth_test_response = await context.client.auth_test(token=user_token) authorize_result = AuthorizeResult.from_auth_test_response( auth_test_response=auth_test_api_response, user_auth_test_response=user_auth_test_response, diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html b/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html index 763fcad3d..e93200864 100644 --- a/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html +++ b/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html @@ -82,7 +82,7 @@

      Args

      """ self.context = context self.logger = context.logger - self.client = context.client # type: ignore[assignment] + self.client = context.client self.enterprise_id = enterprise_id self.team_id = team_id self.user_id = user_id
      diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize.html b/docs/static/api-docs/slack_bolt/authorization/authorize.html index 5534eb3ac..6d5e5526e 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize.html @@ -390,10 +390,10 @@

      Ancestors

      return self.authorize_result_cache[token] try: - auth_test_api_response = context.client.auth_test(token=token) # type: ignore[union-attr] + auth_test_api_response = context.client.auth_test(token=token) user_auth_test_response = None if user_token is not None and token != user_token: - user_auth_test_response = context.client.auth_test(token=user_token) # type: ignore[union-attr] + user_auth_test_response = context.client.auth_test(token=user_token) authorize_result = AuthorizeResult.from_auth_test_response( auth_test_response=auth_test_api_response, user_auth_test_response=user_auth_test_response, diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize_args.html b/docs/static/api-docs/slack_bolt/authorization/authorize_args.html index bd32c1389..fec8531bf 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize_args.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize_args.html @@ -82,7 +82,7 @@

      Args

      """ self.context = context self.logger = context.logger - self.client = context.client # type: ignore[assignment] + self.client = context.client self.enterprise_id = enterprise_id self.team_id = team_id self.user_id = user_id
      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html new file mode 100644 index 000000000..c2d0be5bf --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html @@ -0,0 +1,271 @@ + + + + + + +slack_bolt.context.assistant.assistant_utilities API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.assistant_utilities

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AssistantUtilities +(*, payload: dict, context: BoltContext, thread_context_store: Optional[AssistantThreadContextStore] = None) +
      +
      +
      +
      + +Expand source code + +
      class AssistantUtilities:
      +    payload: dict
      +    client: WebClient
      +    channel_id: str
      +    thread_ts: str
      +    thread_context_store: AssistantThreadContextStore
      +
      +    def __init__(
      +        self,
      +        *,
      +        payload: dict,
      +        context: BoltContext,
      +        thread_context_store: Optional[AssistantThreadContextStore] = None,
      +    ):
      +        self.payload = payload
      +        self.client = context.client
      +        self.thread_context_store = thread_context_store or DefaultAssistantThreadContextStore(context)
      +
      +        if self.payload.get("assistant_thread") is not None:
      +            # assistant_thread_started
      +            thread = self.payload["assistant_thread"]
      +            self.channel_id = thread["channel_id"]
      +            self.thread_ts = thread["thread_ts"]
      +        elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None:
      +            # message event
      +            self.channel_id = self.payload["channel"]
      +            self.thread_ts = self.payload["thread_ts"]
      +        else:
      +            # When moving this code to Bolt internals, no need to raise an exception for this pattern
      +            raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})")
      +
      +    def is_valid(self) -> bool:
      +        return self.channel_id is not None and self.thread_ts is not None
      +
      +    @property
      +    def set_status(self) -> SetStatus:
      +        return SetStatus(self.client, self.channel_id, self.thread_ts)
      +
      +    @property
      +    def set_title(self) -> SetTitle:
      +        return SetTitle(self.client, self.channel_id, self.thread_ts)
      +
      +    @property
      +    def set_suggested_prompts(self) -> SetSuggestedPrompts:
      +        return SetSuggestedPrompts(self.client, self.channel_id, self.thread_ts)
      +
      +    @property
      +    def say(self) -> Say:
      +        return Say(
      +            self.client,
      +            channel=self.channel_id,
      +            thread_ts=self.thread_ts,
      +            metadata={
      +                "event_type": "assistant_thread_context",
      +                "event_payload": self.get_thread_context(),
      +            },
      +        )
      +
      +    @property
      +    def get_thread_context(self) -> GetThreadContext:
      +        return GetThreadContext(self.thread_context_store, self.channel_id, self.thread_ts, self.payload)
      +
      +    @property
      +    def save_thread_context(self) -> SaveThreadContext:
      +        return SaveThreadContext(self.thread_context_store, self.channel_id, self.thread_ts)
      +
      +

      Class variables

      +
      +
      var channel_id : str
      +
      +
      +
      +
      var client : slack_sdk.web.client.WebClient
      +
      +
      +
      +
      var payload : dict
      +
      +
      +
      +
      var thread_context_storeAssistantThreadContextStore
      +
      +
      +
      +
      var thread_ts : str
      +
      +
      +
      +
      +

      Instance variables

      +
      +
      prop get_thread_contextGetThreadContext
      +
      +
      +
      + +Expand source code + +
      @property
      +def get_thread_context(self) -> GetThreadContext:
      +    return GetThreadContext(self.thread_context_store, self.channel_id, self.thread_ts, self.payload)
      +
      +
      +
      prop save_thread_contextSaveThreadContext
      +
      +
      +
      + +Expand source code + +
      @property
      +def save_thread_context(self) -> SaveThreadContext:
      +    return SaveThreadContext(self.thread_context_store, self.channel_id, self.thread_ts)
      +
      +
      +
      prop saySay
      +
      +
      +
      + +Expand source code + +
      @property
      +def say(self) -> Say:
      +    return Say(
      +        self.client,
      +        channel=self.channel_id,
      +        thread_ts=self.thread_ts,
      +        metadata={
      +            "event_type": "assistant_thread_context",
      +            "event_payload": self.get_thread_context(),
      +        },
      +    )
      +
      +
      +
      prop set_statusSetStatus
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_status(self) -> SetStatus:
      +    return SetStatus(self.client, self.channel_id, self.thread_ts)
      +
      +
      +
      prop set_suggested_promptsSetSuggestedPrompts
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_suggested_prompts(self) -> SetSuggestedPrompts:
      +    return SetSuggestedPrompts(self.client, self.channel_id, self.thread_ts)
      +
      +
      +
      prop set_titleSetTitle
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_title(self) -> SetTitle:
      +    return SetTitle(self.client, self.channel_id, self.thread_ts)
      +
      +
      +
      +

      Methods

      +
      +
      +def is_valid(self) ‑> bool +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html b/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html new file mode 100644 index 000000000..4de1dbddf --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html @@ -0,0 +1,271 @@ + + + + + + +slack_bolt.context.assistant.async_assistant_utilities API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.async_assistant_utilities

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AsyncAssistantUtilities +(*, payload: dict, context: AsyncBoltContext, thread_context_store: Optional[AsyncAssistantThreadContextStore] = None) +
      +
      +
      +
      + +Expand source code + +
      class AsyncAssistantUtilities:
      +    payload: dict
      +    client: AsyncWebClient
      +    channel_id: str
      +    thread_ts: str
      +    thread_context_store: AsyncAssistantThreadContextStore
      +
      +    def __init__(
      +        self,
      +        *,
      +        payload: dict,
      +        context: AsyncBoltContext,
      +        thread_context_store: Optional[AsyncAssistantThreadContextStore] = None,
      +    ):
      +        self.payload = payload
      +        self.client = context.client
      +        self.thread_context_store = thread_context_store or DefaultAsyncAssistantThreadContextStore(context)
      +
      +        if self.payload.get("assistant_thread") is not None:
      +            # assistant_thread_started
      +            thread = self.payload["assistant_thread"]
      +            self.channel_id = thread["channel_id"]
      +            self.thread_ts = thread["thread_ts"]
      +        elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None:
      +            # message event
      +            self.channel_id = self.payload["channel"]
      +            self.thread_ts = self.payload["thread_ts"]
      +        else:
      +            # When moving this code to Bolt internals, no need to raise an exception for this pattern
      +            raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})")
      +
      +    def is_valid(self) -> bool:
      +        return self.channel_id is not None and self.thread_ts is not None
      +
      +    @property
      +    def set_status(self) -> AsyncSetStatus:
      +        return AsyncSetStatus(self.client, self.channel_id, self.thread_ts)
      +
      +    @property
      +    def set_title(self) -> AsyncSetTitle:
      +        return AsyncSetTitle(self.client, self.channel_id, self.thread_ts)
      +
      +    @property
      +    def set_suggested_prompts(self) -> AsyncSetSuggestedPrompts:
      +        return AsyncSetSuggestedPrompts(self.client, self.channel_id, self.thread_ts)
      +
      +    @property
      +    def say(self) -> AsyncSay:
      +        return AsyncSay(
      +            self.client,
      +            channel=self.channel_id,
      +            thread_ts=self.thread_ts,
      +            build_metadata=self._build_message_metadata,
      +        )
      +
      +    async def _build_message_metadata(self) -> dict:
      +        return {
      +            "event_type": "assistant_thread_context",
      +            "event_payload": await self.get_thread_context(),
      +        }
      +
      +    @property
      +    def get_thread_context(self) -> AsyncGetThreadContext:
      +        return AsyncGetThreadContext(self.thread_context_store, self.channel_id, self.thread_ts, self.payload)
      +
      +    @property
      +    def save_thread_context(self) -> AsyncSaveThreadContext:
      +        return AsyncSaveThreadContext(self.thread_context_store, self.channel_id, self.thread_ts)
      +
      +

      Class variables

      +
      +
      var channel_id : str
      +
      +
      +
      +
      var client : slack_sdk.web.async_client.AsyncWebClient
      +
      +
      +
      +
      var payload : dict
      +
      +
      +
      +
      var thread_context_storeAsyncAssistantThreadContextStore
      +
      +
      +
      +
      var thread_ts : str
      +
      +
      +
      +
      +

      Instance variables

      +
      +
      prop get_thread_contextAsyncGetThreadContext
      +
      +
      +
      + +Expand source code + +
      @property
      +def get_thread_context(self) -> AsyncGetThreadContext:
      +    return AsyncGetThreadContext(self.thread_context_store, self.channel_id, self.thread_ts, self.payload)
      +
      +
      +
      prop save_thread_contextAsyncSaveThreadContext
      +
      +
      +
      + +Expand source code + +
      @property
      +def save_thread_context(self) -> AsyncSaveThreadContext:
      +    return AsyncSaveThreadContext(self.thread_context_store, self.channel_id, self.thread_ts)
      +
      +
      +
      prop sayAsyncSay
      +
      +
      +
      + +Expand source code + +
      @property
      +def say(self) -> AsyncSay:
      +    return AsyncSay(
      +        self.client,
      +        channel=self.channel_id,
      +        thread_ts=self.thread_ts,
      +        build_metadata=self._build_message_metadata,
      +    )
      +
      +
      +
      prop set_statusAsyncSetStatus
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_status(self) -> AsyncSetStatus:
      +    return AsyncSetStatus(self.client, self.channel_id, self.thread_ts)
      +
      +
      +
      prop set_suggested_promptsAsyncSetSuggestedPrompts
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_suggested_prompts(self) -> AsyncSetSuggestedPrompts:
      +    return AsyncSetSuggestedPrompts(self.client, self.channel_id, self.thread_ts)
      +
      +
      +
      prop set_titleAsyncSetTitle
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_title(self) -> AsyncSetTitle:
      +    return AsyncSetTitle(self.client, self.channel_id, self.thread_ts)
      +
      +
      +
      +

      Methods

      +
      +
      +def is_valid(self) ‑> bool +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/index.html b/docs/static/api-docs/slack_bolt/context/assistant/index.html new file mode 100644 index 000000000..c19bafdea --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/index.html @@ -0,0 +1,82 @@ + + + + + + +slack_bolt.context.assistant API documentation + + + + + + + + + + + +
      + + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html new file mode 100644 index 000000000..59121d712 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html @@ -0,0 +1,121 @@ + + + + + + +slack_bolt.context.assistant.thread_context API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.thread_context

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AssistantThreadContext +(payload: dict) +
      +
      +

      dict() -> new empty dictionary +dict(mapping) -> new dictionary initialized from a mapping object's +(key, value) pairs +dict(iterable) -> new dictionary initialized as if via: +d = {} +for k, v in iterable: +d[k] = v +dict(**kwargs) -> new dictionary initialized with the name=value pairs +in the keyword argument list. +For example: +dict(one=1, two=2)

      +
      + +Expand source code + +
      class AssistantThreadContext(dict):
      +    enterprise_id: Optional[str]
      +    team_id: Optional[str]
      +    channel_id: str
      +
      +    def __init__(self, payload: dict):
      +        dict.__init__(self, **payload)
      +        self.enterprise_id = payload.get("enterprise_id")
      +        self.team_id = payload.get("team_id")
      +        self.channel_id = payload["channel_id"]
      +
      +

      Ancestors

      +
        +
      • builtins.dict
      • +
      +

      Class variables

      +
      +
      var channel_id : str
      +
      +
      +
      +
      var enterprise_id : Optional[str]
      +
      +
      +
      +
      var team_id : Optional[str]
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html new file mode 100644 index 000000000..e7d54ace9 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html @@ -0,0 +1,105 @@ + + + + + + +slack_bolt.context.assistant.thread_context_store.async_store API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.thread_context_store.async_store

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AsyncAssistantThreadContextStore +
      +
      +
      +
      + +Expand source code + +
      class AsyncAssistantThreadContextStore:
      +    async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
      +        raise NotImplementedError()
      +
      +    async def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
      +        raise NotImplementedError()
      +
      +

      Subclasses

      + +

      Methods

      +
      +
      +async def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +
      +
      +
      +
      +
      +async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html new file mode 100644 index 000000000..d0bd8d9cd --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html @@ -0,0 +1,156 @@ + + + + + + +slack_bolt.context.assistant.thread_context_store.default_async_store API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.thread_context_store.default_async_store

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class DefaultAsyncAssistantThreadContextStore +(context: AsyncBoltContext) +
      +
      +
      +
      + +Expand source code + +
      class DefaultAsyncAssistantThreadContextStore(AsyncAssistantThreadContextStore):
      +    client: AsyncWebClient
      +    context: AsyncBoltContext
      +
      +    def __init__(self, context: AsyncBoltContext):
      +        self.client = context.client
      +        self.context = context
      +
      +    async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
      +        parent_message = await self._retrieve_first_bot_reply(channel_id, thread_ts)
      +        if parent_message is not None:
      +            await self.client.chat_update(
      +                channel=channel_id,
      +                ts=parent_message["ts"],
      +                text=parent_message["text"],
      +                blocks=parent_message["blocks"],
      +                metadata={
      +                    "event_type": "assistant_thread_context",
      +                    "event_payload": context,
      +                },
      +            )
      +
      +    async def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
      +        parent_message = await self._retrieve_first_bot_reply(channel_id, thread_ts)
      +        if parent_message is not None and parent_message.get("metadata"):
      +            if bool(parent_message["metadata"]["event_payload"]):
      +                return AssistantThreadContext(parent_message["metadata"]["event_payload"])
      +        return None
      +
      +    async def _retrieve_first_bot_reply(self, channel_id: str, thread_ts: str) -> Optional[dict]:
      +        messages: List[dict] = (
      +            await self.client.conversations_replies(
      +                channel=channel_id,
      +                ts=thread_ts,
      +                oldest=thread_ts,
      +                include_all_metadata=True,
      +                limit=4,  # 2 should be usually enough but buffer for more robustness
      +            )
      +        ).get("messages", [])
      +        for message in messages:
      +            if message.get("subtype") is None and message.get("user") == self.context.bot_user_id:
      +                return message
      +        return None
      +
      +

      Ancestors

      + +

      Class variables

      +
      +
      var client : slack_sdk.web.async_client.AsyncWebClient
      +
      +
      +
      +
      var contextAsyncBoltContext
      +
      +
      +
      +
      +

      Methods

      +
      +
      +async def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +
      +
      +
      +
      +
      +async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html new file mode 100644 index 000000000..7d53db27b --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html @@ -0,0 +1,154 @@ + + + + + + +slack_bolt.context.assistant.thread_context_store.default_store API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.thread_context_store.default_store

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class DefaultAssistantThreadContextStore +(context: BoltContext) +
      +
      +
      +
      + +Expand source code + +
      class DefaultAssistantThreadContextStore(AssistantThreadContextStore):
      +    client: WebClient
      +    context: "BoltContext"
      +
      +    def __init__(self, context: BoltContext):
      +        self.client = context.client
      +        self.context = context
      +
      +    def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
      +        parent_message = self._retrieve_first_bot_reply(channel_id, thread_ts)
      +        if parent_message is not None:
      +            self.client.chat_update(
      +                channel=channel_id,
      +                ts=parent_message["ts"],
      +                text=parent_message["text"],
      +                blocks=parent_message["blocks"],
      +                metadata={
      +                    "event_type": "assistant_thread_context",
      +                    "event_payload": context,
      +                },
      +            )
      +
      +    def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
      +        parent_message = self._retrieve_first_bot_reply(channel_id, thread_ts)
      +        if parent_message is not None and parent_message.get("metadata"):
      +            if bool(parent_message["metadata"]["event_payload"]):
      +                return AssistantThreadContext(parent_message["metadata"]["event_payload"])
      +        return None
      +
      +    def _retrieve_first_bot_reply(self, channel_id: str, thread_ts: str) -> Optional[dict]:
      +        messages: List[dict] = self.client.conversations_replies(
      +            channel=channel_id,
      +            ts=thread_ts,
      +            oldest=thread_ts,
      +            include_all_metadata=True,
      +            limit=4,  # 2 should be usually enough but buffer for more robustness
      +        ).get("messages", [])
      +        for message in messages:
      +            if message.get("subtype") is None and message.get("user") == self.context.bot_user_id:
      +                return message
      +        return None
      +
      +

      Ancestors

      + +

      Class variables

      +
      +
      var client : slack_sdk.web.client.WebClient
      +
      +
      +
      +
      var contextBoltContext
      +
      +
      +
      +
      +

      Methods

      +
      +
      +def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +
      +
      +
      +
      +
      +def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html new file mode 100644 index 000000000..d4ee18a6a --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html @@ -0,0 +1,130 @@ + + + + + + +slack_bolt.context.assistant.thread_context_store.file API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.thread_context_store.file

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class FileAssistantThreadContextStore +(base_dir: str = '/Users/kazuhiro.sera/.bolt-app-assistant-thread-contexts') +
      +
      +
      +
      + +Expand source code + +
      class FileAssistantThreadContextStore(AssistantThreadContextStore):
      +
      +    def __init__(
      +        self,
      +        base_dir: str = str(Path.home()) + "/.bolt-app-assistant-thread-contexts",
      +    ):
      +        self.base_dir = base_dir
      +        self._mkdir(self.base_dir)
      +
      +    def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
      +        path = f"{self.base_dir}/{channel_id}-{thread_ts}.json"
      +        with open(path, "w") as f:
      +            f.write(json.dumps(context))
      +
      +    def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
      +        path = f"{self.base_dir}/{channel_id}-{thread_ts}.json"
      +        try:
      +            with open(path) as f:
      +                data = json.loads(f.read())
      +                if data.get("channel_id") is not None:
      +                    return AssistantThreadContext(data)
      +        except FileNotFoundError:
      +            pass
      +        return None
      +
      +    @staticmethod
      +    def _mkdir(path: Union[str, Path]):
      +        if isinstance(path, str):
      +            path = Path(path)
      +        path.mkdir(parents=True, exist_ok=True)
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +
      +
      +
      +
      +
      +def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html new file mode 100644 index 000000000..19e88534b --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html @@ -0,0 +1,87 @@ + + + + + + +slack_bolt.context.assistant.thread_context_store API documentation + + + + + + + + + + + +
      + + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html new file mode 100644 index 000000000..247791024 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html @@ -0,0 +1,106 @@ + + + + + + +slack_bolt.context.assistant.thread_context_store.store API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.context.assistant.thread_context_store.store

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AssistantThreadContextStore +
      +
      +
      +
      + +Expand source code + +
      class AssistantThreadContextStore:
      +    def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
      +        raise NotImplementedError()
      +
      +    def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
      +        raise NotImplementedError()
      +
      +

      Subclasses

      + +

      Methods

      +
      +
      +def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +
      +
      +
      +
      +
      +def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + diff --git a/docs/static/api-docs/slack_bolt/context/async_context.html b/docs/static/api-docs/slack_bolt/context/async_context.html index 0bd0311bc..146d13b98 100644 --- a/docs/static/api-docs/slack_bolt/context/async_context.html +++ b/docs/static/api-docs/slack_bolt/context/async_context.html @@ -51,22 +51,31 @@

      Classes

      def to_copyable(self) -> "AsyncBoltContext": new_dict = {} for prop_name, prop_value in self.items(): - if prop_name in self.standard_property_names: + if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value + elif prop_name in self.non_copyable_standard_property_names: + # Do nothing with this property (e.g., listener_runner) + continue else: try: copied_value = create_copy(prop_value) new_dict[prop_name] = copied_value except TypeError as te: self.logger.debug( - f"Skipped settings '{prop_name}' to a copied request for lazy listeners " + f"Skipped setting '{prop_name}' to a copied request for lazy listeners " f"as it's not possible to make a deep copy (error: {te})" ) return AsyncBoltContext(new_dict) + # The return type is intentionally string to avoid circular imports @property - def client(self) -> Optional[AsyncWebClient]: + def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defined] + """The properly configured listener_runner that is available for middleware/listeners.""" + return self["listener_runner"] + + @property + def client(self) -> AsyncWebClient: """The `AsyncWebClient` instance available for this request. @app.event("app_mention") @@ -130,7 +139,7 @@

      Classes

      Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"] @property @@ -154,8 +163,8 @@

      Classes

      if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"] @@ -180,9 +189,7 @@

      Classes

      Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property @@ -206,10 +213,28 @@

      Classes

      Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) - return self["fail"] + self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) + return self["fail"] + + @property + def set_title(self) -> Optional[AsyncSetTitle]: + return self.get("set_title") + + @property + def set_status(self) -> Optional[AsyncSetStatus]: + return self.get("set_status") + + @property + def set_suggested_prompts(self) -> Optional[AsyncSetSuggestedPrompts]: + return self.get("set_suggested_prompts") + + @property + def get_thread_context(self) -> Optional[AsyncGetThreadContext]: + return self.get("get_thread_context") + + @property + def save_thread_context(self) -> Optional[AsyncSaveThreadContext]: + return self.get("save_thread_context")

      Ancestors

        @@ -257,7 +282,7 @@

        Returns

        return self["ack"] -
        prop client : Optional[slack_sdk.web.async_client.AsyncWebClient]
        +
        prop client : slack_sdk.web.async_client.AsyncWebClient

        The AsyncWebClient instance available for this request.

        @app.event("app_mention")
        @@ -282,7 +307,7 @@ 

        Returns

        Expand source code
        @property
        -def client(self) -> Optional[AsyncWebClient]:
        +def client(self) -> AsyncWebClient:
             """The `AsyncWebClient` instance available for this request.
         
                 @app.event("app_mention")
        @@ -351,9 +376,7 @@ 

        Returns

        Callable `complete()` function """ if "complete" not in self: - self["complete"] = AsyncComplete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = AsyncComplete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"]
        @@ -400,12 +423,35 @@

        Returns

        Callable `fail()` function """ if "fail" not in self: - self["fail"] = AsyncFail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["fail"] = AsyncFail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"]
        +
        prop get_thread_context : Optional[AsyncGetThreadContext]
        +
        +
        +
        + +Expand source code + +
        @property
        +def get_thread_context(self) -> Optional[AsyncGetThreadContext]:
        +    return self.get("get_thread_context")
        +
        +
        +
        prop listener_runner : AsyncioListenerRunner
        +
        +

        The properly configured listener_runner that is available for middleware/listeners.

        +
        + +Expand source code + +
        @property
        +def listener_runner(self) -> "AsyncioListenerRunner":  # type: ignore[name-defined]
        +    """The properly configured listener_runner that is available for middleware/listeners."""
        +    return self["listener_runner"]
        +
        +
        prop respond : Optional[AsyncRespond]

        respond() function for this request.

        @@ -447,12 +493,24 @@

        Returns

        if "respond" not in self: self["respond"] = AsyncRespond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"]
        +
        prop save_thread_context : Optional[AsyncSaveThreadContext]
        +
        +
        +
        + +Expand source code + +
        @property
        +def save_thread_context(self) -> Optional[AsyncSaveThreadContext]:
        +    return self.get("save_thread_context")
        +
        +
        prop sayAsyncSay

        say() function for this request.

        @@ -492,10 +550,46 @@

        Returns

        Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"]
        +
        prop set_status : Optional[AsyncSetStatus]
        +
        +
        +
        + +Expand source code + +
        @property
        +def set_status(self) -> Optional[AsyncSetStatus]:
        +    return self.get("set_status")
        +
        +
        +
        prop set_suggested_prompts : Optional[AsyncSetSuggestedPrompts]
        +
        +
        +
        + +Expand source code + +
        @property
        +def set_suggested_prompts(self) -> Optional[AsyncSetSuggestedPrompts]:
        +    return self.get("set_suggested_prompts")
        +
        +
        +
        prop set_title : Optional[AsyncSetTitle]
        +
        +
        +
        + +Expand source code + +
        @property
        +def set_title(self) -> Optional[AsyncSetTitle]:
        +    return self.get("set_title")
        +
        +

        Methods

        @@ -527,6 +621,7 @@

        Inherited members

      • matches
      • response_url
      • team_id
      • +
      • thread_ts
      • token
      • user_id
      • user_token
      • @@ -551,13 +646,19 @@

        Inherited members

        • AsyncBoltContext

          -
            + diff --git a/docs/static/api-docs/slack_bolt/context/base_context.html b/docs/static/api-docs/slack_bolt/context/base_context.html index 3c06dcb81..587a5efd5 100644 --- a/docs/static/api-docs/slack_bolt/context/base_context.html +++ b/docs/static/api-docs/slack_bolt/context/base_context.html @@ -48,7 +48,7 @@

            Classes

            class BaseContext(dict):
                 """Context object associated with a request from Slack."""
             
            -    standard_property_names = [
            +    copyable_standard_property_names = [
                     "logger",
                     "token",
                     "enterprise_id",
            @@ -59,6 +59,7 @@ 

            Classes

            "actor_team_id", "actor_user_id", "channel_id", + "thread_ts", "response_url", "matches", "authorize_result", @@ -75,7 +76,21 @@

            Classes

            "respond", "complete", "fail", + "set_status", + "set_title", + "set_suggested_prompts", ] + # Note that these items are not copyable, so when you add new items to this list, + # you must modify ThreadListenerRunner/AsyncioListenerRunner's _build_lazy_request method to pass the values. + # Other listener runners do not require the change because they invoke a lazy listener over the network, + # meaning that the context initialization would be done again. + non_copyable_standard_property_names = [ + "listener_runner", + "get_thread_context", + "save_thread_context", + ] + + standard_property_names = copyable_standard_property_names + non_copyable_standard_property_names @property def logger(self) -> Logger: @@ -136,6 +151,11 @@

            Classes

            """The conversation ID associated with this request.""" return self.get("channel_id") + @property + def thread_ts(self) -> Optional[str]: + """The conversation thread's ID associated with this request.""" + return self.get("thread_ts") + @property def response_url(self) -> Optional[str]: """The `response_url` associated with this request.""" @@ -218,6 +238,14 @@

            Subclasses

          Class variables

          +
          var copyable_standard_property_names
          +
          +
          +
          +
          var non_copyable_standard_property_names
          +
          +
          +
          var standard_property_names
          @@ -470,6 +498,19 @@

          Instance variables

          return self.get("team_id")
          +
          prop thread_ts : Optional[str]
          +
          +

          The conversation thread's ID associated with this request.

          +
          + +Expand source code + +
          @property
          +def thread_ts(self) -> Optional[str]:
          +    """The conversation thread's ID associated with this request."""
          +    return self.get("thread_ts")
          +
          +
          prop token : Optional[str]

          The (bot/user) token resolved for this request.

          @@ -546,6 +587,7 @@

          bot_token

        • bot_user_id
        • channel_id
        • +
        • copyable_standard_property_names
        • enterprise_id
        • function_bot_access_token
        • function_execution_id
        • @@ -553,10 +595,12 @@

          is_enterprise_install
        • logger
        • matches
        • +
        • non_copyable_standard_property_names
        • response_url
        • set_authorize_result
        • standard_property_names
        • team_id
        • +
        • thread_ts
        • token
        • user_id
        • user_token
        • diff --git a/docs/static/api-docs/slack_bolt/context/context.html b/docs/static/api-docs/slack_bolt/context/context.html index 32fb34b86..04a9af74d 100644 --- a/docs/static/api-docs/slack_bolt/context/context.html +++ b/docs/static/api-docs/slack_bolt/context/context.html @@ -51,9 +51,12 @@

          Classes

          def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): - if prop_name in self.standard_property_names: + if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value + elif prop_name in self.non_copyable_standard_property_names: + # Do nothing with this property (e.g., listener_runner) + continue else: try: copied_value = create_copy(prop_value) @@ -66,8 +69,14 @@

          Classes

          ) return BoltContext(new_dict) + # The return type is intentionally string to avoid circular imports @property - def client(self) -> Optional[WebClient]: + def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + """The properly configured listener_runner that is available for middleware/listeners.""" + return self["listener_runner"] + + @property + def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") @@ -131,7 +140,7 @@

          Classes

          Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id) + self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"] @property @@ -155,8 +164,8 @@

          Classes

          if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"] @@ -181,9 +190,7 @@

          Classes

          Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property @@ -207,10 +214,28 @@

          Classes

          Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) - return self["fail"] + self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + return self["fail"] + + @property + def set_title(self) -> Optional[SetTitle]: + return self.get("set_title") + + @property + def set_status(self) -> Optional[SetStatus]: + return self.get("set_status") + + @property + def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: + return self.get("set_suggested_prompts") + + @property + def get_thread_context(self) -> Optional[GetThreadContext]: + return self.get("get_thread_context") + + @property + def save_thread_context(self) -> Optional[SaveThreadContext]: + return self.get("save_thread_context")

          Ancestors

            @@ -258,7 +283,7 @@

            Returns

            return self["ack"] -
            prop client : Optional[slack_sdk.web.client.WebClient]
            +
            prop client : slack_sdk.web.client.WebClient

            The WebClient instance available for this request.

            @app.event("app_mention")
            @@ -283,7 +308,7 @@ 

            Returns

            Expand source code
            @property
            -def client(self) -> Optional[WebClient]:
            +def client(self) -> WebClient:
                 """The `WebClient` instance available for this request.
             
                     @app.event("app_mention")
            @@ -352,9 +377,7 @@ 

            Returns

            Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"]
            @@ -401,12 +424,35 @@

            Returns

            Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"]
            +
            prop get_thread_context : Optional[GetThreadContext]
            +
            +
            +
            + +Expand source code + +
            @property
            +def get_thread_context(self) -> Optional[GetThreadContext]:
            +    return self.get("get_thread_context")
            +
            +
            +
            prop listener_runner : ThreadListenerRunner
            +
            +

            The properly configured listener_runner that is available for middleware/listeners.

            +
            + +Expand source code + +
            @property
            +def listener_runner(self) -> "ThreadListenerRunner":  # type: ignore[name-defined]
            +    """The properly configured listener_runner that is available for middleware/listeners."""
            +    return self["listener_runner"]
            +
            +
            prop respond : Optional[Respond]

            respond() function for this request.

            @@ -448,12 +494,24 @@

            Returns

            if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"]
            +
            prop save_thread_context : Optional[SaveThreadContext]
            +
            +
            +
            + +Expand source code + +
            @property
            +def save_thread_context(self) -> Optional[SaveThreadContext]:
            +    return self.get("save_thread_context")
            +
            +
            prop saySay

            say() function for this request.

            @@ -493,10 +551,46 @@

            Returns

            Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id) + self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"]
            +
            prop set_status : Optional[SetStatus]
            +
            +
            +
            + +Expand source code + +
            @property
            +def set_status(self) -> Optional[SetStatus]:
            +    return self.get("set_status")
            +
            +
            +
            prop set_suggested_prompts : Optional[SetSuggestedPrompts]
            +
            +
            +
            + +Expand source code + +
            @property
            +def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]:
            +    return self.get("set_suggested_prompts")
            +
            +
            +
            prop set_title : Optional[SetTitle]
            +
            +
            +
            + +Expand source code + +
            @property
            +def set_title(self) -> Optional[SetTitle]:
            +    return self.get("set_title")
            +
            +

        Methods

        @@ -528,6 +622,7 @@

        Inherited members

      • matches
      • response_url
      • team_id
      • +
      • thread_ts
      • token
      • user_id
      • user_token
      • @@ -552,13 +647,19 @@

        Inherited members

        • BoltContext

          -
            + diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html b/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html new file mode 100644 index 000000000..5db376357 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html @@ -0,0 +1,149 @@ + + + + + + +slack_bolt.context.get_thread_context.async_get_thread_context API documentation + + + + + + + + + + + +
            +
            +
            +

            Module slack_bolt.context.get_thread_context.async_get_thread_context

            +
            +
            +
            +
            +
            +
            +
            +
            +
            +
            +

            Classes

            +
            +
            +class AsyncGetThreadContext +(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +
            +
            +
            +
            + +Expand source code + +
            class AsyncGetThreadContext:
            +    thread_context_store: AsyncAssistantThreadContextStore
            +    payload: dict
            +    channel_id: str
            +    thread_ts: str
            +
            +    _thread_context: Optional[AssistantThreadContext]
            +    thread_context_loaded: bool
            +
            +    def __init__(
            +        self,
            +        thread_context_store: AsyncAssistantThreadContextStore,
            +        channel_id: str,
            +        thread_ts: str,
            +        payload: dict,
            +    ):
            +        self.thread_context_store = thread_context_store
            +        self.payload = payload
            +        self.channel_id = channel_id
            +        self.thread_ts = thread_ts
            +        self._thread_context: Optional[AssistantThreadContext] = None
            +        self.thread_context_loaded = False
            +
            +    async def __call__(self) -> Optional[AssistantThreadContext]:
            +        if self.thread_context_loaded is True:
            +            return self._thread_context
            +
            +        if self.payload.get("assistant_thread") is not None:
            +            # assistant_thread_started
            +            thread = self.payload["assistant_thread"]
            +            self._thread_context = (
            +                AssistantThreadContext(thread["context"])
            +                if thread.get("context", {}).get("channel_id") is not None
            +                else None
            +            )
            +            # for this event, the context will never be changed
            +            self.thread_context_loaded = True
            +        elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None:
            +            # message event
            +            self._thread_context = await self.thread_context_store.find(channel_id=self.channel_id, thread_ts=self.thread_ts)
            +
            +        return self._thread_context
            +
            +

            Class variables

            +
            +
            var channel_id : str
            +
            +
            +
            +
            var payload : dict
            +
            +
            +
            +
            var thread_context_loaded : bool
            +
            +
            +
            +
            var thread_context_storeAsyncAssistantThreadContextStore
            +
            +
            +
            +
            var thread_ts : str
            +
            +
            +
            +
            +
            +
            +
            +
            + +
            + + + diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html b/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html new file mode 100644 index 000000000..887cc1525 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html @@ -0,0 +1,149 @@ + + + + + + +slack_bolt.context.get_thread_context.get_thread_context API documentation + + + + + + + + + + + +
            +
            +
            +

            Module slack_bolt.context.get_thread_context.get_thread_context

            +
            +
            +
            +
            +
            +
            +
            +
            +
            +
            +

            Classes

            +
            +
            +class GetThreadContext +(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +
            +
            +
            +
            + +Expand source code + +
            class GetThreadContext:
            +    thread_context_store: AssistantThreadContextStore
            +    payload: dict
            +    channel_id: str
            +    thread_ts: str
            +
            +    _thread_context: Optional[AssistantThreadContext]
            +    thread_context_loaded: bool
            +
            +    def __init__(
            +        self,
            +        thread_context_store: AssistantThreadContextStore,
            +        channel_id: str,
            +        thread_ts: str,
            +        payload: dict,
            +    ):
            +        self.thread_context_store = thread_context_store
            +        self.payload = payload
            +        self.channel_id = channel_id
            +        self.thread_ts = thread_ts
            +        self._thread_context: Optional[AssistantThreadContext] = None
            +        self.thread_context_loaded = False
            +
            +    def __call__(self) -> Optional[AssistantThreadContext]:
            +        if self.thread_context_loaded is True:
            +            return self._thread_context
            +
            +        if self.payload.get("assistant_thread") is not None:
            +            # assistant_thread_started
            +            thread = self.payload["assistant_thread"]
            +            self._thread_context = (
            +                AssistantThreadContext(thread["context"])
            +                if thread.get("context", {}).get("channel_id") is not None
            +                else None
            +            )
            +            # for this event, the context will never be changed
            +            self.thread_context_loaded = True
            +        elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None:
            +            # message event
            +            self._thread_context = self.thread_context_store.find(channel_id=self.channel_id, thread_ts=self.thread_ts)
            +
            +        return self._thread_context
            +
            +

            Class variables

            +
            +
            var channel_id : str
            +
            +
            +
            +
            var payload : dict
            +
            +
            +
            +
            var thread_context_loaded : bool
            +
            +
            +
            +
            var thread_context_storeAssistantThreadContextStore
            +
            +
            +
            +
            var thread_ts : str
            +
            +
            +
            +
            +
            +
            +
            +
            + +
            + + + diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html b/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html new file mode 100644 index 000000000..71d68842d --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html @@ -0,0 +1,166 @@ + + + + + + +slack_bolt.context.get_thread_context API documentation + + + + + + + + + + + +
            +
            +
            +

            Module slack_bolt.context.get_thread_context

            +
            +
            +
            +
            +

            Sub-modules

            +
            +
            slack_bolt.context.get_thread_context.async_get_thread_context
            +
            +
            +
            +
            slack_bolt.context.get_thread_context.get_thread_context
            +
            +
            +
            +
            +
            +
            +
            +
            +
            +
            +

            Classes

            +
            +
            +class GetThreadContext +(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +
            +
            +
            +
            + +Expand source code + +
            class GetThreadContext:
            +    thread_context_store: AssistantThreadContextStore
            +    payload: dict
            +    channel_id: str
            +    thread_ts: str
            +
            +    _thread_context: Optional[AssistantThreadContext]
            +    thread_context_loaded: bool
            +
            +    def __init__(
            +        self,
            +        thread_context_store: AssistantThreadContextStore,
            +        channel_id: str,
            +        thread_ts: str,
            +        payload: dict,
            +    ):
            +        self.thread_context_store = thread_context_store
            +        self.payload = payload
            +        self.channel_id = channel_id
            +        self.thread_ts = thread_ts
            +        self._thread_context: Optional[AssistantThreadContext] = None
            +        self.thread_context_loaded = False
            +
            +    def __call__(self) -> Optional[AssistantThreadContext]:
            +        if self.thread_context_loaded is True:
            +            return self._thread_context
            +
            +        if self.payload.get("assistant_thread") is not None:
            +            # assistant_thread_started
            +            thread = self.payload["assistant_thread"]
            +            self._thread_context = (
            +                AssistantThreadContext(thread["context"])
            +                if thread.get("context", {}).get("channel_id") is not None
            +                else None
            +            )
            +            # for this event, the context will never be changed
            +            self.thread_context_loaded = True
            +        elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None:
            +            # message event
            +            self._thread_context = self.thread_context_store.find(channel_id=self.channel_id, thread_ts=self.thread_ts)
            +
            +        return self._thread_context
            +
            +

            Class variables

            +
            +
            var channel_id : str
            +
            +
            +
            +
            var payload : dict
            +
            +
            +
            +
            var thread_context_loaded : bool
            +
            +
            +
            +
            var thread_context_storeAssistantThreadContextStore
            +
            +
            +
            +
            var thread_ts : str
            +
            +
            +
            +
            +
            +
            +
            +
            + +
            + + + diff --git a/docs/static/api-docs/slack_bolt/context/index.html b/docs/static/api-docs/slack_bolt/context/index.html index 341403877..b25e85281 100644 --- a/docs/static/api-docs/slack_bolt/context/index.html +++ b/docs/static/api-docs/slack_bolt/context/index.html @@ -38,6 +38,10 @@

            Sub-modules

            +
            slack_bolt.context.assistant
            +
            +
            +
            slack_bolt.context.async_context
            @@ -58,14 +62,34 @@

            Sub-modules

            +
            slack_bolt.context.get_thread_context
            +
            +
            +
            slack_bolt.context.respond
            +
            slack_bolt.context.save_thread_context
            +
            +
            +
            slack_bolt.context.say
            +
            slack_bolt.context.set_status
            +
            +
            +
            +
            slack_bolt.context.set_suggested_prompts
            +
            +
            +
            +
            slack_bolt.context.set_title
            +
            +
            +
        @@ -91,9 +115,12 @@

        Classes

        def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): - if prop_name in self.standard_property_names: + if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value + elif prop_name in self.non_copyable_standard_property_names: + # Do nothing with this property (e.g., listener_runner) + continue else: try: copied_value = create_copy(prop_value) @@ -106,8 +133,14 @@

        Classes

        ) return BoltContext(new_dict) + # The return type is intentionally string to avoid circular imports + @property + def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + """The properly configured listener_runner that is available for middleware/listeners.""" + return self["listener_runner"] + @property - def client(self) -> Optional[WebClient]: + def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") @@ -171,7 +204,7 @@

        Classes

        Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id) + self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"] @property @@ -195,8 +228,8 @@

        Classes

        if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"] @@ -221,9 +254,7 @@

        Classes

        Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property @@ -247,10 +278,28 @@

        Classes

        Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) - return self["fail"] + self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + return self["fail"] + + @property + def set_title(self) -> Optional[SetTitle]: + return self.get("set_title") + + @property + def set_status(self) -> Optional[SetStatus]: + return self.get("set_status") + + @property + def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: + return self.get("set_suggested_prompts") + + @property + def get_thread_context(self) -> Optional[GetThreadContext]: + return self.get("get_thread_context") + + @property + def save_thread_context(self) -> Optional[SaveThreadContext]: + return self.get("save_thread_context")

        Ancestors

          @@ -298,7 +347,7 @@

          Returns

          return self["ack"] -
          prop client : Optional[slack_sdk.web.client.WebClient]
          +
          prop client : slack_sdk.web.client.WebClient

          The WebClient instance available for this request.

          @app.event("app_mention")
          @@ -323,7 +372,7 @@ 

          Returns

          Expand source code
          @property
          -def client(self) -> Optional[WebClient]:
          +def client(self) -> WebClient:
               """The `WebClient` instance available for this request.
           
                   @app.event("app_mention")
          @@ -392,9 +441,7 @@ 

          Returns

          Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"]
          @@ -441,12 +488,35 @@

          Returns

          Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"]
          +
          prop get_thread_context : Optional[GetThreadContext]
          +
          +
          +
          + +Expand source code + +
          @property
          +def get_thread_context(self) -> Optional[GetThreadContext]:
          +    return self.get("get_thread_context")
          +
          +
          +
          prop listener_runner : ThreadListenerRunner
          +
          +

          The properly configured listener_runner that is available for middleware/listeners.

          +
          + +Expand source code + +
          @property
          +def listener_runner(self) -> "ThreadListenerRunner":  # type: ignore[name-defined]
          +    """The properly configured listener_runner that is available for middleware/listeners."""
          +    return self["listener_runner"]
          +
          +
          prop respond : Optional[Respond]

          slack_bolt.context.respond function for this request.

          @@ -488,12 +558,24 @@

          Returns

          if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"]
          +
          prop save_thread_context : Optional[SaveThreadContext]
          +
          +
          +
          + +Expand source code + +
          @property
          +def save_thread_context(self) -> Optional[SaveThreadContext]:
          +    return self.get("save_thread_context")
          +
          +
          prop saySay

          slack_bolt.context.say function for this request.

          @@ -533,10 +615,46 @@

          Returns

          Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id) + self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"]
          +
          prop set_status : Optional[SetStatus]
          +
          +
          +
          + +Expand source code + +
          @property
          +def set_status(self) -> Optional[SetStatus]:
          +    return self.get("set_status")
          +
          +
          +
          prop set_suggested_prompts : Optional[SetSuggestedPrompts]
          +
          +
          +
          + +Expand source code + +
          @property
          +def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]:
          +    return self.get("set_suggested_prompts")
          +
          +
          +
          prop set_title : Optional[SetTitle]
          +
          +
          +
          + +Expand source code + +
          @property
          +def set_title(self) -> Optional[SetTitle]:
          +    return self.get("set_title")
          +
          +

          Methods

          @@ -568,6 +686,7 @@

          Inherited members

        • matches
        • response_url
        • team_id
        • +
        • thread_ts
        • token
        • user_id
        • user_token
        • @@ -591,26 +710,38 @@

          Inherited members

        • Sub-modules

        • Classes

          • BoltContext

            -
              + diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html b/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html new file mode 100644 index 000000000..d1e5af19c --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html @@ -0,0 +1,118 @@ + + + + + + +slack_bolt.context.save_thread_context.async_save_thread_context API documentation + + + + + + + + + + + +
              +
              +
              +

              Module slack_bolt.context.save_thread_context.async_save_thread_context

              +
              +
              +
              +
              +
              +
              +
              +
              +
              +
              +

              Classes

              +
              +
              +class AsyncSaveThreadContext +(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str) +
              +
              +
              +
              + +Expand source code + +
              class AsyncSaveThreadContext:
              +    thread_context_store: AsyncAssistantThreadContextStore
              +    channel_id: str
              +    thread_ts: str
              +
              +    def __init__(
              +        self,
              +        thread_context_store: AsyncAssistantThreadContextStore,
              +        channel_id: str,
              +        thread_ts: str,
              +    ):
              +        self.thread_context_store = thread_context_store
              +        self.channel_id = channel_id
              +        self.thread_ts = thread_ts
              +
              +    async def __call__(self, new_context: Dict[str, str]) -> None:
              +        await self.thread_context_store.save(
              +            channel_id=self.channel_id,
              +            thread_ts=self.thread_ts,
              +            context=new_context,
              +        )
              +
              +

              Class variables

              +
              +
              var channel_id : str
              +
              +
              +
              +
              var thread_context_storeAsyncAssistantThreadContextStore
              +
              +
              +
              +
              var thread_ts : str
              +
              +
              +
              +
              +
              +
              +
              +
              + +
              + + + diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html b/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html new file mode 100644 index 000000000..0b593b7c2 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html @@ -0,0 +1,135 @@ + + + + + + +slack_bolt.context.save_thread_context API documentation + + + + + + + + + + + +
              +
              +
              +

              Module slack_bolt.context.save_thread_context

              +
              +
              +
              +
              +

              Sub-modules

              +
              +
              slack_bolt.context.save_thread_context.async_save_thread_context
              +
              +
              +
              +
              slack_bolt.context.save_thread_context.save_thread_context
              +
              +
              +
              +
              +
              +
              +
              +
              +
              +
              +

              Classes

              +
              +
              +class SaveThreadContext +(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str) +
              +
              +
              +
              + +Expand source code + +
              class SaveThreadContext:
              +    thread_context_store: AssistantThreadContextStore
              +    channel_id: str
              +    thread_ts: str
              +
              +    def __init__(
              +        self,
              +        thread_context_store: AssistantThreadContextStore,
              +        channel_id: str,
              +        thread_ts: str,
              +    ):
              +        self.thread_context_store = thread_context_store
              +        self.channel_id = channel_id
              +        self.thread_ts = thread_ts
              +
              +    def __call__(self, new_context: Dict[str, str]) -> None:
              +        self.thread_context_store.save(
              +            channel_id=self.channel_id,
              +            thread_ts=self.thread_ts,
              +            context=new_context,
              +        )
              +
              +

              Class variables

              +
              +
              var channel_id : str
              +
              +
              +
              +
              var thread_context_storeAssistantThreadContextStore
              +
              +
              +
              +
              var thread_ts : str
              +
              +
              +
              +
              +
              +
              +
              +
              + +
              + + + diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html b/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html new file mode 100644 index 000000000..6a693a49e --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html @@ -0,0 +1,118 @@ + + + + + + +slack_bolt.context.save_thread_context.save_thread_context API documentation + + + + + + + + + + + +
              +
              +
              +

              Module slack_bolt.context.save_thread_context.save_thread_context

              +
              +
              +
              +
              +
              +
              +
              +
              +
              +
              +

              Classes

              +
              +
              +class SaveThreadContext +(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str) +
              +
              +
              +
              + +Expand source code + +
              class SaveThreadContext:
              +    thread_context_store: AssistantThreadContextStore
              +    channel_id: str
              +    thread_ts: str
              +
              +    def __init__(
              +        self,
              +        thread_context_store: AssistantThreadContextStore,
              +        channel_id: str,
              +        thread_ts: str,
              +    ):
              +        self.thread_context_store = thread_context_store
              +        self.channel_id = channel_id
              +        self.thread_ts = thread_ts
              +
              +    def __call__(self, new_context: Dict[str, str]) -> None:
              +        self.thread_context_store.save(
              +            channel_id=self.channel_id,
              +            thread_ts=self.thread_ts,
              +            context=new_context,
              +        )
              +
              +

              Class variables

              +
              +
              var channel_id : str
              +
              +
              +
              +
              var thread_context_storeAssistantThreadContextStore
              +
              +
              +
              +
              var thread_ts : str
              +
              +
              +
              +
              +
              +
              +
              +
              + +
              + + + diff --git a/docs/static/api-docs/slack_bolt/context/say/async_say.html b/docs/static/api-docs/slack_bolt/context/say/async_say.html index 47577bddd..d4ca5ca65 100644 --- a/docs/static/api-docs/slack_bolt/context/say/async_say.html +++ b/docs/static/api-docs/slack_bolt/context/say/async_say.html @@ -37,7 +37,7 @@

              Classes

              class AsyncSay -(client: Optional[slack_sdk.web.async_client.AsyncWebClient], channel: Optional[str]) +(client: Optional[slack_sdk.web.async_client.AsyncWebClient], channel: Optional[str], thread_ts: Optional[str] = None, build_metadata: Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]] = None)
              @@ -48,14 +48,20 @@

              Classes

              class AsyncSay:
                   client: Optional[AsyncWebClient]
                   channel: Optional[str]
              +    thread_ts: Optional[str]
              +    build_metadata: Optional[Callable[[], Awaitable[Union[Dict, Metadata]]]]
               
                   def __init__(
                       self,
                       client: Optional[AsyncWebClient],
                       channel: Optional[str],
              +        thread_ts: Optional[str] = None,
              +        build_metadata: Optional[Callable[[], Awaitable[Union[Dict, Metadata]]]] = None,
                   ):
                       self.client = client
                       self.channel = channel
              +        self.thread_ts = thread_ts
              +        self.build_metadata = build_metadata
               
                   async def __call__(
                       self,
              @@ -78,6 +84,8 @@ 

              Classes

              **kwargs, ) -> AsyncSlackResponse: if _can_say(self, channel): + if metadata is None and self.build_metadata is not None: + metadata = await self.build_metadata() text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response @@ -87,7 +95,7 @@

              Classes

              blocks=blocks, attachments=attachments, as_user=as_user, - thread_ts=thread_ts, + thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, @@ -104,6 +112,10 @@

              Classes

              message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel + if "thread_ts" not in message: + message["thread_ts"] = thread_ts or self.thread_ts + if "metadata" not in message: + message["metadata"] = metadata return await self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -112,6 +124,10 @@

              Classes

              Class variables

              +
              var build_metadata : Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]]
              +
              +
              +
              var channel : Optional[str]
              @@ -120,6 +136,10 @@

              Class variables

              +
              var thread_ts : Optional[str]
              +
              +
              +
              @@ -140,8 +160,10 @@

              Class variables

            • AsyncSay

            diff --git a/docs/static/api-docs/slack_bolt/context/say/index.html b/docs/static/api-docs/slack_bolt/context/say/index.html index f6fd5bb4d..7799c5a21 100644 --- a/docs/static/api-docs/slack_bolt/context/say/index.html +++ b/docs/static/api-docs/slack_bolt/context/say/index.html @@ -52,7 +52,7 @@

            Classes

            class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str]) +(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None)
            @@ -63,14 +63,20 @@

            Classes

            class Say:
                 client: Optional[WebClient]
                 channel: Optional[str]
            +    thread_ts: Optional[str]
            +    metadata: Optional[Union[Dict, Metadata]]
             
                 def __init__(
                     self,
                     client: Optional[WebClient],
                     channel: Optional[str],
            +        thread_ts: Optional[str] = None,
            +        metadata: Optional[Union[Dict, Metadata]] = None,
                 ):
                     self.client = client
                     self.channel = channel
            +        self.thread_ts = thread_ts
            +        self.metadata = metadata
             
                 def __call__(
                     self,
            @@ -102,7 +108,7 @@ 

            Classes

            blocks=blocks, attachments=attachments, as_user=as_user, - thread_ts=thread_ts, + thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, @@ -112,13 +118,17 @@

            Classes

            mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata, + metadata=metadata or self.metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel + if "thread_ts" not in message: + message["thread_ts"] = thread_ts or self.thread_ts + if "metadata" not in message: + message["metadata"] = metadata or self.metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -135,6 +145,14 @@

            Class variables

            +
            var metadata : Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]
            +
            +
            +
            +
            var thread_ts : Optional[str]
            +
            +
            +
        • @@ -164,6 +182,8 @@

        • channel
        • client
        • +
        • metadata
        • +
        • thread_ts
      diff --git a/docs/static/api-docs/slack_bolt/context/say/say.html b/docs/static/api-docs/slack_bolt/context/say/say.html index bfbc1a677..e25077d20 100644 --- a/docs/static/api-docs/slack_bolt/context/say/say.html +++ b/docs/static/api-docs/slack_bolt/context/say/say.html @@ -37,7 +37,7 @@

      Classes

      class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str]) +(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None)
      @@ -48,14 +48,20 @@

      Classes

      class Say:
           client: Optional[WebClient]
           channel: Optional[str]
      +    thread_ts: Optional[str]
      +    metadata: Optional[Union[Dict, Metadata]]
       
           def __init__(
               self,
               client: Optional[WebClient],
               channel: Optional[str],
      +        thread_ts: Optional[str] = None,
      +        metadata: Optional[Union[Dict, Metadata]] = None,
           ):
               self.client = client
               self.channel = channel
      +        self.thread_ts = thread_ts
      +        self.metadata = metadata
       
           def __call__(
               self,
      @@ -87,7 +93,7 @@ 

      Classes

      blocks=blocks, attachments=attachments, as_user=as_user, - thread_ts=thread_ts, + thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, @@ -97,13 +103,17 @@

      Classes

      mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata, + metadata=metadata or self.metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel + if "thread_ts" not in message: + message["thread_ts"] = thread_ts or self.thread_ts + if "metadata" not in message: + message["metadata"] = metadata or self.metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -120,6 +130,14 @@

      Class variables

      +
      var metadata : Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]
      +
      +
      +
      +
      var thread_ts : Optional[str]
      +
      +
      +
      @@ -142,6 +160,8 @@

    • channel
    • client
    • +
    • metadata
    • +
    • thread_ts
  • diff --git a/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html b/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html new file mode 100644 index 000000000..0a05750ae --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html @@ -0,0 +1,118 @@ + + + + + + +slack_bolt.context.set_status.async_set_status API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_status.async_set_status

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class AsyncSetStatus +(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class AsyncSetStatus:
    +    client: AsyncWebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: AsyncWebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(self, status: str) -> AsyncSlackResponse:
    +        return await self.client.assistant_threads_setStatus(
    +            status=status,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_status/index.html b/docs/static/api-docs/slack_bolt/context/set_status/index.html new file mode 100644 index 000000000..72e084e96 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_status/index.html @@ -0,0 +1,135 @@ + + + + + + +slack_bolt.context.set_status API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_status

    +
    +
    +
    +
    +

    Sub-modules

    +
    +
    slack_bolt.context.set_status.async_set_status
    +
    +
    +
    +
    slack_bolt.context.set_status.set_status
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SetStatus +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class SetStatus:
    +    client: WebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: WebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(self, status: str) -> SlackResponse:
    +        return self.client.assistant_threads_setStatus(
    +            status=status,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_status/set_status.html b/docs/static/api-docs/slack_bolt/context/set_status/set_status.html new file mode 100644 index 000000000..869d61a2f --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_status/set_status.html @@ -0,0 +1,118 @@ + + + + + + +slack_bolt.context.set_status.set_status API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_status.set_status

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SetStatus +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class SetStatus:
    +    client: WebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: WebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(self, status: str) -> SlackResponse:
    +        return self.client.assistant_threads_setStatus(
    +            status=status,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html new file mode 100644 index 000000000..a553aa59d --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html @@ -0,0 +1,125 @@ + + + + + + +slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class AsyncSetSuggestedPrompts +(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class AsyncSetSuggestedPrompts:
    +    client: AsyncWebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: AsyncWebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> AsyncSlackResponse:
    +        prompts_arg: List[Dict[str, str]] = []
    +        for prompt in prompts:
    +            if isinstance(prompt, str):
    +                prompts_arg.append({"title": prompt, "message": prompt})
    +            else:
    +                prompts_arg.append(prompt)
    +
    +        return await self.client.assistant_threads_setSuggestedPrompts(
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +            prompts=prompts_arg,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html new file mode 100644 index 000000000..bc591bbad --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html @@ -0,0 +1,142 @@ + + + + + + +slack_bolt.context.set_suggested_prompts API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_suggested_prompts

    +
    +
    +
    +
    +

    Sub-modules

    +
    +
    slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts
    +
    +
    +
    +
    slack_bolt.context.set_suggested_prompts.set_suggested_prompts
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SetSuggestedPrompts +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class SetSuggestedPrompts:
    +    client: WebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: WebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse:
    +        prompts_arg: List[Dict[str, str]] = []
    +        for prompt in prompts:
    +            if isinstance(prompt, str):
    +                prompts_arg.append({"title": prompt, "message": prompt})
    +            else:
    +                prompts_arg.append(prompt)
    +
    +        return self.client.assistant_threads_setSuggestedPrompts(
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +            prompts=prompts_arg,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html new file mode 100644 index 000000000..685518619 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html @@ -0,0 +1,125 @@ + + + + + + +slack_bolt.context.set_suggested_prompts.set_suggested_prompts API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_suggested_prompts.set_suggested_prompts

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SetSuggestedPrompts +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class SetSuggestedPrompts:
    +    client: WebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: WebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse:
    +        prompts_arg: List[Dict[str, str]] = []
    +        for prompt in prompts:
    +            if isinstance(prompt, str):
    +                prompts_arg.append({"title": prompt, "message": prompt})
    +            else:
    +                prompts_arg.append(prompt)
    +
    +        return self.client.assistant_threads_setSuggestedPrompts(
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +            prompts=prompts_arg,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html b/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html new file mode 100644 index 000000000..388ab25ce --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html @@ -0,0 +1,118 @@ + + + + + + +slack_bolt.context.set_title.async_set_title API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_title.async_set_title

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class AsyncSetTitle +(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class AsyncSetTitle:
    +    client: AsyncWebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: AsyncWebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(self, title: str) -> AsyncSlackResponse:
    +        return await self.client.assistant_threads_setTitle(
    +            title=title,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_title/index.html b/docs/static/api-docs/slack_bolt/context/set_title/index.html new file mode 100644 index 000000000..41192c77f --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_title/index.html @@ -0,0 +1,135 @@ + + + + + + +slack_bolt.context.set_title API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_title

    +
    +
    +
    +
    +

    Sub-modules

    +
    +
    slack_bolt.context.set_title.async_set_title
    +
    +
    +
    +
    slack_bolt.context.set_title.set_title
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SetTitle +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class SetTitle:
    +    client: WebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: WebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(self, title: str) -> SlackResponse:
    +        return self.client.assistant_threads_setTitle(
    +            title=title,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/context/set_title/set_title.html b/docs/static/api-docs/slack_bolt/context/set_title/set_title.html new file mode 100644 index 000000000..07faff2c3 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/set_title/set_title.html @@ -0,0 +1,118 @@ + + + + + + +slack_bolt.context.set_title.set_title API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.set_title.set_title

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SetTitle +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
    +
    +
    +
    + +Expand source code + +
    class SetTitle:
    +    client: WebClient
    +    channel_id: str
    +    thread_ts: str
    +
    +    def __init__(
    +        self,
    +        client: WebClient,
    +        channel_id: str,
    +        thread_ts: str,
    +    ):
    +        self.client = client
    +        self.channel_id = channel_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(self, title: str) -> SlackResponse:
    +        return self.client.assistant_threads_setTitle(
    +            title=title,
    +            channel_id=self.channel_id,
    +            thread_ts=self.thread_ts,
    +        )
    +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +
    +
    +
    var thread_ts : str
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/static/api-docs/slack_bolt/index.html b/docs/static/api-docs/slack_bolt/index.html index ff34a124d..17fc98dfe 100644 --- a/docs/static/api-docs/slack_bolt/index.html +++ b/docs/static/api-docs/slack_bolt/index.html @@ -177,7 +177,7 @@

    Class variables

    class App -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None) +(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None, assistant_thread_context_store: Optional[AssistantThreadContextStore] = None)

    Bolt App that provides functionalities to register middleware/listeners.

    @@ -247,6 +247,10 @@

    Args

    False if you would like to disable the built-in middleware (Default: True). IgnoringSelfEvents is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
    +
    ignoring_self_assistant_message_events_enabled
    +
    False if you would like to disable the built-in middleware. +IgnoringSelfEvents for this app's bot user message events within an assistant thread +This is useful for avoiding code error causing an infinite loop; Default: True
    url_verification_enabled
    False if you would like to disable the built-in middleware (Default: True). UrlVerification is a built-in middleware that handles url_verification requests @@ -267,6 +271,9 @@

    Args

    listener_executor
    Custom executor to run background tasks. If absent, the default ThreadPoolExecutor will be used.
    +
    assistant_thread_context_store
    +
    Custom AssistantThreadContext store (Default: the built-in implementation, +which uses a parent message's metadata to store the latest context)
    @@ -299,6 +306,7 @@

    Args

    # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -309,6 +317,8 @@

    Args

    verification_token: Optional[str] = None, # Set this one only when you want to customize the executor listener_executor: Optional[Executor] = None, + # for AI Agents & Assistants + assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -364,6 +374,9 @@

    Args

    ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). + ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. + `IgnoringSelfEvents` for this app's bot user message events within an assistant thread + This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. @@ -377,6 +390,8 @@

    Args

    verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. + assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, + which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") @@ -523,6 +538,8 @@

    Args

    if listener_executor is None: listener_executor = ThreadPoolExecutor(max_workers=5) + self._assistant_thread_context_store = assistant_thread_context_store + self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( logger=self._framework_logger, @@ -545,6 +562,7 @@

    Args

    token_verification_enabled=token_verification_enabled, request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, @@ -556,6 +574,7 @@

    Args

    token_verification_enabled: bool = True, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, + ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, @@ -616,7 +635,12 @@

    Args

    raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: - self._middleware_list.append(IgnoringSelfEvents(base_logger=self._base_logger)) + self._middleware_list.append( + IgnoringSelfEvents( + base_logger=self._base_logger, + ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, + ) + ) if url_verification_enabled is True: self._middleware_list.append(UrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: @@ -841,6 +865,8 @@

    Args

    if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) + if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: + self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( @@ -854,6 +880,12 @@

    Args

    raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None + # ------------------------- + # AI Agents & Assistants + + def assistant(self, assistant: Assistant) -> Optional[Callable]: + return self.middleware(assistant) + # ------------------------- # Workflows: Steps from apps @@ -920,7 +952,7 @@

    Args

    elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") - self.use(WorkflowStepMiddleware(step, self.listener_runner)) + self.use(WorkflowStepMiddleware(step)) # ------------------------- # global error handler @@ -1062,6 +1094,7 @@

    Args

    callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, + auto_acknowledge: bool = True, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -1096,7 +1129,7 @@

    Args

    def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, True) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) return __call__ @@ -1535,6 +1568,24 @@

    Args

    ) req.context["client"] = client_per_request + # Most apps do not need this "listener_runner" instance. + # It is intended for apps that start lazy listeners from their custom global middleware. + req.context["listener_runner"] = self.listener_runner + + # For AI Agents & Assistants + if is_assistant_event(req.body): + assistant = AssistantUtilities( + payload=to_event(req.body), # type:ignore[arg-type] + context=req.context, + thread_context_store=self._assistant_thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + @staticmethod def _to_listener_functions( kwargs: dict, @@ -1714,6 +1765,12 @@

    Args

    Only when all the middleware call next() method, the listener function can be invoked. +
    +def assistant(self, assistant: Assistant) ‑> Optional[Callable] +
    +
    +
    +
    def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]
    @@ -1870,7 +1927,7 @@

    Args

    -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]

    Registers a new Function listener. @@ -2166,7 +2223,7 @@

    Args

    class Args -(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, next: Callable[[], None], **kwargs) +(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, next: Callable[[], None], **kwargs)

    All the arguments in this class are available in any middleware / listeners. @@ -2272,6 +2329,16 @@

    Args

    """`complete()` utility function, signals a successful completion of the custom function""" fail: Fail """`fail()` utility function, signal that the custom function failed to complete""" + set_status: Optional[SetStatus] + """`set_status()` utility function for AI Agents & Assistants""" + set_title: Optional[SetTitle] + """`set_title()` utility function for AI Agents & Assistants""" + set_suggested_prompts: Optional[SetSuggestedPrompts] + """`set_suggested_prompts()` utility function for AI Agents & Assistants""" + get_thread_context: Optional[GetThreadContext] + """`get_thread_context()` utility function for AI Agents & Assistants""" + save_thread_context: Optional[SaveThreadContext] + """`save_thread_context()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -2300,11 +2367,16 @@

    Args

    respond: Respond, complete: Complete, fail: Fail, + set_status: Optional[SetStatus] = None, + set_title: Optional[SetTitle] = None, + set_suggested_prompts: Optional[SetSuggestedPrompts] = None, + get_thread_context: Optional[GetThreadContext] = None, + save_thread_context: Optional[SaveThreadContext] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], None], - **kwargs # noqa + **kwargs, # noqa ): self.logger: logging.Logger = logger self.client: WebClient = client @@ -2327,6 +2399,13 @@

    Args

    self.respond: Respond = respond self.complete: Complete = complete self.fail: Fail = fail + + self.set_status = set_status + self.set_title = set_title + self.set_suggested_prompts = set_suggested_prompts + self.get_thread_context = get_thread_context + self.save_thread_context = save_thread_context + self.next: Callable[[], None] = next self.next_: Callable[[], None] = next
    @@ -2368,6 +2447,10 @@

    Class variables

    fail() utility function, signal that the custom function failed to complete

    +
    var get_thread_context : Optional[GetThreadContext]
    +
    +

    get_thread_context() utility function for AI Agents & Assistants

    +
    var logger : logging.Logger

    Logger instance

    @@ -2412,10 +2495,26 @@

    Class variables

    Response representation

    +
    var save_thread_context : Optional[SaveThreadContext]
    +
    +

    save_thread_context() utility function for AI Agents & Assistants

    +
    var saySay

    say() utility function, which calls chat.postMessage API with the associated channel ID

    +
    var set_status : Optional[SetStatus]
    +
    +

    set_status() utility function for AI Agents & Assistants

    +
    +
    var set_suggested_prompts : Optional[SetSuggestedPrompts]
    +
    +

    set_suggested_prompts() utility function for AI Agents & Assistants

    +
    +
    var set_title : Optional[SetTitle]
    +
    +

    set_title() utility function for AI Agents & Assistants

    +
    var shortcut : Optional[Dict[str, Any]]

    An alias for payload in an @app.shortcut listener

    @@ -2426,6 +2525,435 @@

    Class variables

    +
    +class Assistant +(*, app_name: str = 'assistant', thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +
    +
    +

    A middleware can process request data before other middleware and listener functions.

    +
    + +Expand source code + +
    class Assistant(Middleware):
    +    _thread_started_listeners: Optional[List[Listener]]
    +    _thread_context_changed_listeners: Optional[List[Listener]]
    +    _user_message_listeners: Optional[List[Listener]]
    +    _bot_message_listeners: Optional[List[Listener]]
    +
    +    thread_context_store: Optional[AssistantThreadContextStore]
    +    base_logger: Optional[logging.Logger]
    +
    +    def __init__(
    +        self,
    +        *,
    +        app_name: str = "assistant",
    +        thread_context_store: Optional[AssistantThreadContextStore] = None,
    +        logger: Optional[logging.Logger] = None,
    +    ):
    +        self.app_name = app_name
    +        self.thread_context_store = thread_context_store
    +        self.base_logger = logger
    +
    +        self._thread_started_listeners = None
    +        self._thread_context_changed_listeners = None
    +        self._user_message_listeners = None
    +        self._bot_message_listeners = None
    +
    +    def thread_started(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, Middleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._thread_started_listeners is None:
    +            self._thread_started_listeners = []
    +        all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers)
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._thread_started_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._thread_started_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    def user_message(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, Middleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._user_message_listeners is None:
    +            self._user_message_listeners = []
    +        all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers)
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._user_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._user_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    def bot_message(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, Middleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._bot_message_listeners is None:
    +            self._bot_message_listeners = []
    +        all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers)
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._bot_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._bot_message_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    def thread_context_changed(
    +        self,
    +        *args,
    +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
    +        middleware: Optional[Union[Callable, Middleware]] = None,
    +        lazy: Optional[List[Callable[..., None]]] = None,
    +    ):
    +        if self._thread_context_changed_listeners is None:
    +            self._thread_context_changed_listeners = []
    +        all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers)
    +        if is_used_without_argument(args):
    +            func = args[0]
    +            self._thread_context_changed_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=func,
    +                    matchers=all_matchers,
    +                    middleware=middleware,  # type:ignore[arg-type]
    +                )
    +            )
    +            return func
    +
    +        def _inner(func):
    +            functions = [func] + (lazy if lazy is not None else [])
    +            self._thread_context_changed_listeners.append(
    +                self.build_listener(
    +                    listener_or_functions=functions,
    +                    matchers=all_matchers,
    +                    middleware=middleware,
    +                )
    +            )
    +
    +            @wraps(func)
    +            def _wrapper(*args, **kwargs):
    +                return func(*args, **kwargs)
    +
    +            return _wrapper
    +
    +        return _inner
    +
    +    def _merge_matchers(
    +        self,
    +        primary_matcher: Callable[..., bool],
    +        custom_matchers: Optional[Union[Callable[..., bool], ListenerMatcher]],
    +    ):
    +        return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + (
    +            custom_matchers or []
    +        )  # type:ignore[operator]
    +
    +    @staticmethod
    +    def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict):
    +        save_thread_context(payload["assistant_thread"]["context"])
    +
    +    def process(  # type:ignore[return]
    +        self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]
    +    ) -> Optional[BoltResponse]:
    +        if self._thread_context_changed_listeners is None:
    +            self.thread_context_changed(self.default_thread_context_changed)
    +
    +        listener_runner: ThreadListenerRunner = req.context.listener_runner
    +        for listeners in [
    +            self._thread_started_listeners,
    +            self._thread_context_changed_listeners,
    +            self._user_message_listeners,
    +            self._bot_message_listeners,
    +        ]:
    +            if listeners is not None:
    +                for listener in listeners:
    +                    if listener.matches(req=req, resp=resp):
    +                        return listener_runner.run(
    +                            request=req,
    +                            response=resp,
    +                            listener_name="assistant_listener",
    +                            listener=listener,
    +                        )
    +        if is_other_message_sub_event_in_assistant_thread(req.body):
    +            # message_changed, message_deleted, etc.
    +            return req.context.ack()
    +
    +        next()
    +
    +    def build_listener(
    +        self,
    +        listener_or_functions: Union[Listener, Callable, List[Callable]],
    +        matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None,
    +        middleware: Optional[List[Middleware]] = None,
    +        base_logger: Optional[Logger] = None,
    +    ) -> Listener:
    +        if isinstance(listener_or_functions, Callable):  # type:ignore[arg-type]
    +            listener_or_functions = [listener_or_functions]  # type:ignore[list-item]
    +
    +        if isinstance(listener_or_functions, Listener):
    +            return listener_or_functions
    +        elif isinstance(listener_or_functions, list):
    +            middleware = middleware if middleware else []
    +            functions = listener_or_functions
    +            ack_function = functions.pop(0)
    +
    +            matchers = matchers if matchers else []
    +            listener_matchers: List[ListenerMatcher] = []
    +            for matcher in matchers:
    +                if isinstance(matcher, ListenerMatcher):
    +                    listener_matchers.append(matcher)
    +                elif isinstance(matcher, Callable):  # type:ignore[arg-type]
    +                    listener_matchers.append(
    +                        build_listener_matcher(
    +                            func=matcher,
    +                            asyncio=False,
    +                            base_logger=base_logger,
    +                        )
    +                    )
    +            return CustomListener(
    +                app_name=self.app_name,
    +                matchers=listener_matchers,
    +                middleware=middleware,
    +                ack_function=ack_function,
    +                lazy_functions=functions,
    +                auto_acknowledgement=True,
    +                base_logger=base_logger or self.base_logger,
    +            )
    +        else:
    +            raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected")
    +
    +

    Ancestors

    + +

    Class variables

    +
    +
    var base_logger : Optional[logging.Logger]
    +
    +
    +
    +
    var thread_context_store : Optional[AssistantThreadContextStore]
    +
    +
    +
    +
    +

    Static methods

    +
    +
    +def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict) +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def bot_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +def build_listener(self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> Listener +
    +
    +
    +
    +
    +def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +def thread_started(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +def user_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
    +
    +
    +
    +
    +

    Inherited members

    + +
    +
    +class AssistantThreadContext +(payload: dict) +
    +
    +

    dict() -> new empty dictionary +dict(mapping) -> new dictionary initialized from a mapping object's +(key, value) pairs +dict(iterable) -> new dictionary initialized as if via: +d = {} +for k, v in iterable: +d[k] = v +dict(**kwargs) -> new dictionary initialized with the name=value pairs +in the keyword argument list. +For example: +dict(one=1, two=2)

    +
    + +Expand source code + +
    class AssistantThreadContext(dict):
    +    enterprise_id: Optional[str]
    +    team_id: Optional[str]
    +    channel_id: str
    +
    +    def __init__(self, payload: dict):
    +        dict.__init__(self, **payload)
    +        self.enterprise_id = payload.get("enterprise_id")
    +        self.team_id = payload.get("team_id")
    +        self.channel_id = payload["channel_id"]
    +
    +

    Ancestors

    +
      +
    • builtins.dict
    • +
    +

    Class variables

    +
    +
    var channel_id : str
    +
    +
    +
    +
    var enterprise_id : Optional[str]
    +
    +
    +
    +
    var team_id : Optional[str]
    +
    +
    +
    +
    +
    +
    +class AssistantThreadContextStore +
    +
    +
    +
    + +Expand source code + +
    class AssistantThreadContextStore:
    +    def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
    +        raise NotImplementedError()
    +
    +    def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
    +        raise NotImplementedError()
    +
    +

    Subclasses

    + +

    Methods

    +
    +
    +def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +
    +
    +
    +
    +
    +def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None +
    +
    +
    +
    +
    +
    class BoltContext (*args, **kwargs) @@ -2442,9 +2970,12 @@

    Class variables

    def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): - if prop_name in self.standard_property_names: + if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value + elif prop_name in self.non_copyable_standard_property_names: + # Do nothing with this property (e.g., listener_runner) + continue else: try: copied_value = create_copy(prop_value) @@ -2457,8 +2988,14 @@

    Class variables

    ) return BoltContext(new_dict) + # The return type is intentionally string to avoid circular imports @property - def client(self) -> Optional[WebClient]: + def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + """The properly configured listener_runner that is available for middleware/listeners.""" + return self["listener_runner"] + + @property + def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") @@ -2522,7 +3059,7 @@

    Class variables

    Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id) + self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"] @property @@ -2546,8 +3083,8 @@

    Class variables

    if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"] @@ -2572,9 +3109,7 @@

    Class variables

    Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property @@ -2598,10 +3133,28 @@

    Class variables

    Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) - return self["fail"]
    + self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) + return self["fail"] + + @property + def set_title(self) -> Optional[SetTitle]: + return self.get("set_title") + + @property + def set_status(self) -> Optional[SetStatus]: + return self.get("set_status") + + @property + def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: + return self.get("set_suggested_prompts") + + @property + def get_thread_context(self) -> Optional[GetThreadContext]: + return self.get("get_thread_context") + + @property + def save_thread_context(self) -> Optional[SaveThreadContext]: + return self.get("save_thread_context")

    Ancestors

      @@ -2649,7 +3202,7 @@

      Returns

      return self["ack"]
      -
      prop client : Optional[slack_sdk.web.client.WebClient]
      +
      prop client : slack_sdk.web.client.WebClient

      The WebClient instance available for this request.

      @app.event("app_mention")
      @@ -2674,7 +3227,7 @@ 

      Returns

      Expand source code
      @property
      -def client(self) -> Optional[WebClient]:
      +def client(self) -> WebClient:
           """The `WebClient` instance available for this request.
       
               @app.event("app_mention")
      @@ -2743,9 +3296,7 @@ 

      Returns

      Callable `complete()` function """ if "complete" not in self: - self["complete"] = Complete( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"]
      @@ -2792,12 +3343,35 @@

      Returns

      Callable `fail()` function """ if "fail" not in self: - self["fail"] = Fail( - client=self.client, function_execution_id=self.function_execution_id # type: ignore[arg-type] - ) + self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"]
      +
      prop get_thread_context : Optional[GetThreadContext]
      +
      +
      +
      + +Expand source code + +
      @property
      +def get_thread_context(self) -> Optional[GetThreadContext]:
      +    return self.get("get_thread_context")
      +
      +
      +
      prop listener_runner : ThreadListenerRunner
      +
      +

      The properly configured listener_runner that is available for middleware/listeners.

      +
      + +Expand source code + +
      @property
      +def listener_runner(self) -> "ThreadListenerRunner":  # type: ignore[name-defined]
      +    """The properly configured listener_runner that is available for middleware/listeners."""
      +    return self["listener_runner"]
      +
      +
      prop respond : Optional[Respond]

      respond() function for this request.

      @@ -2839,12 +3413,24 @@

      Returns

      if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, - proxy=self.client.proxy, # type: ignore[union-attr] - ssl=self.client.ssl, # type: ignore[union-attr] + proxy=self.client.proxy, + ssl=self.client.ssl, ) return self["respond"]
      +
      prop save_thread_context : Optional[SaveThreadContext]
      +
      +
      +
      + +Expand source code + +
      @property
      +def save_thread_context(self) -> Optional[SaveThreadContext]:
      +    return self.get("save_thread_context")
      +
      +
      prop saySay

      say() function for this request.

      @@ -2884,10 +3470,46 @@

      Returns

      Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id) + self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) return self["say"]
      +
      prop set_status : Optional[SetStatus]
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_status(self) -> Optional[SetStatus]:
      +    return self.get("set_status")
      +
      +
      +
      prop set_suggested_prompts : Optional[SetSuggestedPrompts]
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]:
      +    return self.get("set_suggested_prompts")
      +
      +
      +
      prop set_title : Optional[SetTitle]
      +
      +
      +
      + +Expand source code + +
      @property
      +def set_title(self) -> Optional[SetTitle]:
      +    return self.get("set_title")
      +
      +

      Methods

      @@ -2919,6 +3541,7 @@

      Inherited members

    • matches
    • response_url
    • team_id
    • +
    • thread_ts
    • token
    • user_id
    • user_token
    • @@ -3345,6 +3968,67 @@

      Class variables

      +
      +class FileAssistantThreadContextStore +(base_dir: str = '/Users/kazuhiro.sera/.bolt-app-assistant-thread-contexts') +
      +
      +
      +
      + +Expand source code + +
      class FileAssistantThreadContextStore(AssistantThreadContextStore):
      +
      +    def __init__(
      +        self,
      +        base_dir: str = str(Path.home()) + "/.bolt-app-assistant-thread-contexts",
      +    ):
      +        self.base_dir = base_dir
      +        self._mkdir(self.base_dir)
      +
      +    def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
      +        path = f"{self.base_dir}/{channel_id}-{thread_ts}.json"
      +        with open(path, "w") as f:
      +            f.write(json.dumps(context))
      +
      +    def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
      +        path = f"{self.base_dir}/{channel_id}-{thread_ts}.json"
      +        try:
      +            with open(path) as f:
      +                data = json.loads(f.read())
      +                if data.get("channel_id") is not None:
      +                    return AssistantThreadContext(data)
      +        except FileNotFoundError:
      +            pass
      +        return None
      +
      +    @staticmethod
      +    def _mkdir(path: Union[str, Path]):
      +        if isinstance(path, str):
      +            path = Path(path)
      +        path.mkdir(parents=True, exist_ok=True)
      +
      +

      Ancestors

      + +

      Methods

      +
      +
      +def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +
      +
      +
      +
      +
      +def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None +
      +
      +
      +
      +
      +
      class Listener
      @@ -3566,9 +4250,57 @@

      Class variables

      +
      +class SaveThreadContext +(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str) +
      +
      +
      +
      + +Expand source code + +
      class SaveThreadContext:
      +    thread_context_store: AssistantThreadContextStore
      +    channel_id: str
      +    thread_ts: str
      +
      +    def __init__(
      +        self,
      +        thread_context_store: AssistantThreadContextStore,
      +        channel_id: str,
      +        thread_ts: str,
      +    ):
      +        self.thread_context_store = thread_context_store
      +        self.channel_id = channel_id
      +        self.thread_ts = thread_ts
      +
      +    def __call__(self, new_context: Dict[str, str]) -> None:
      +        self.thread_context_store.save(
      +            channel_id=self.channel_id,
      +            thread_ts=self.thread_ts,
      +            context=new_context,
      +        )
      +
      +

      Class variables

      +
      +
      var channel_id : str
      +
      +
      +
      +
      var thread_context_storeAssistantThreadContextStore
      +
      +
      +
      +
      var thread_ts : str
      +
      +
      +
      +
      +
      class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str]) +(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None)
      @@ -3579,14 +4311,20 @@

      Class variables

      class Say:
           client: Optional[WebClient]
           channel: Optional[str]
      +    thread_ts: Optional[str]
      +    metadata: Optional[Union[Dict, Metadata]]
       
           def __init__(
               self,
               client: Optional[WebClient],
               channel: Optional[str],
      +        thread_ts: Optional[str] = None,
      +        metadata: Optional[Union[Dict, Metadata]] = None,
           ):
               self.client = client
               self.channel = channel
      +        self.thread_ts = thread_ts
      +        self.metadata = metadata
       
           def __call__(
               self,
      @@ -3618,7 +4356,7 @@ 

      Class variables

      blocks=blocks, attachments=attachments, as_user=as_user, - thread_ts=thread_ts, + thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, @@ -3628,13 +4366,17 @@

      Class variables

      mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata, + metadata=metadata or self.metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel + if "thread_ts" not in message: + message["thread_ts"] = thread_ts or self.thread_ts + if "metadata" not in message: + message["metadata"] = metadata or self.metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -3651,6 +4393,165 @@

      Class variables

      +
      var metadata : Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]
      +
      +
      +
      +
      var thread_ts : Optional[str]
      +
      +
      +
      + +
      +
      +class SetStatus +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
      +
      +
      +
      + +Expand source code + +
      class SetStatus:
      +    client: WebClient
      +    channel_id: str
      +    thread_ts: str
      +
      +    def __init__(
      +        self,
      +        client: WebClient,
      +        channel_id: str,
      +        thread_ts: str,
      +    ):
      +        self.client = client
      +        self.channel_id = channel_id
      +        self.thread_ts = thread_ts
      +
      +    def __call__(self, status: str) -> SlackResponse:
      +        return self.client.assistant_threads_setStatus(
      +            status=status,
      +            channel_id=self.channel_id,
      +            thread_ts=self.thread_ts,
      +        )
      +
      +

      Class variables

      +
      +
      var channel_id : str
      +
      +
      +
      +
      var client : slack_sdk.web.client.WebClient
      +
      +
      +
      +
      var thread_ts : str
      +
      +
      +
      +
      +
      +
      +class SetSuggestedPrompts +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
      +
      +
      +
      + +Expand source code + +
      class SetSuggestedPrompts:
      +    client: WebClient
      +    channel_id: str
      +    thread_ts: str
      +
      +    def __init__(
      +        self,
      +        client: WebClient,
      +        channel_id: str,
      +        thread_ts: str,
      +    ):
      +        self.client = client
      +        self.channel_id = channel_id
      +        self.thread_ts = thread_ts
      +
      +    def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse:
      +        prompts_arg: List[Dict[str, str]] = []
      +        for prompt in prompts:
      +            if isinstance(prompt, str):
      +                prompts_arg.append({"title": prompt, "message": prompt})
      +            else:
      +                prompts_arg.append(prompt)
      +
      +        return self.client.assistant_threads_setSuggestedPrompts(
      +            channel_id=self.channel_id,
      +            thread_ts=self.thread_ts,
      +            prompts=prompts_arg,
      +        )
      +
      +

      Class variables

      +
      +
      var channel_id : str
      +
      +
      +
      +
      var client : slack_sdk.web.client.WebClient
      +
      +
      +
      +
      var thread_ts : str
      +
      +
      +
      +
      +
      +
      +class SetTitle +(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +
      +
      +
      +
      + +Expand source code + +
      class SetTitle:
      +    client: WebClient
      +    channel_id: str
      +    thread_ts: str
      +
      +    def __init__(
      +        self,
      +        client: WebClient,
      +        channel_id: str,
      +        thread_ts: str,
      +    ):
      +        self.client = client
      +        self.channel_id = channel_id
      +        self.thread_ts = thread_ts
      +
      +    def __call__(self, title: str) -> SlackResponse:
      +        return self.client.assistant_threads_setTitle(
      +            title=title,
      +            channel_id=self.channel_id,
      +            thread_ts=self.thread_ts,
      +        )
      +
      +

      Class variables

      +
      +
      var channel_id : str
      +
      +
      +
      +
      var client : slack_sdk.web.client.WebClient
      +
      +
      +
      +
      var thread_ts : str
      +
      +
      +
      @@ -3695,6 +4596,7 @@

      Ack

      App

      • action
      • +
      • assistant
      • attachment_action
      • block_action
      • block_suggestion
      • @@ -3732,7 +4634,7 @@

        App

      • Args

        -
          +
        • +

          Assistant

          + +
        • +
        • +

          AssistantThreadContext

          + +
        • +
        • +

          AssistantThreadContextStore

          + +
        • +
        • BoltContext

          -
            + @@ -3820,6 +4761,13 @@

            Fail

          • +

            FileAssistantThreadContextStore

            + +
          • +
          • Listener

            diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/args.html b/docs/static/api-docs/slack_bolt/kwargs_injection/args.html index f555b3aff..6768b4230 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/args.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/args.html @@ -37,7 +37,7 @@

            Classes

            class Args -(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, next: Callable[[], None], **kwargs) +(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, next: Callable[[], None], **kwargs)

            All the arguments in this class are available in any middleware / listeners. @@ -143,6 +143,16 @@

            Classes

            """`complete()` utility function, signals a successful completion of the custom function""" fail: Fail """`fail()` utility function, signal that the custom function failed to complete""" + set_status: Optional[SetStatus] + """`set_status()` utility function for AI Agents & Assistants""" + set_title: Optional[SetTitle] + """`set_title()` utility function for AI Agents & Assistants""" + set_suggested_prompts: Optional[SetSuggestedPrompts] + """`set_suggested_prompts()` utility function for AI Agents & Assistants""" + get_thread_context: Optional[GetThreadContext] + """`get_thread_context()` utility function for AI Agents & Assistants""" + save_thread_context: Optional[SaveThreadContext] + """`save_thread_context()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -171,11 +181,16 @@

            Classes

            respond: Respond, complete: Complete, fail: Fail, + set_status: Optional[SetStatus] = None, + set_title: Optional[SetTitle] = None, + set_suggested_prompts: Optional[SetSuggestedPrompts] = None, + get_thread_context: Optional[GetThreadContext] = None, + save_thread_context: Optional[SaveThreadContext] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], None], - **kwargs # noqa + **kwargs, # noqa ): self.logger: logging.Logger = logger self.client: WebClient = client @@ -198,6 +213,13 @@

            Classes

            self.respond: Respond = respond self.complete: Complete = complete self.fail: Fail = fail + + self.set_status = set_status + self.set_title = set_title + self.set_suggested_prompts = set_suggested_prompts + self.get_thread_context = get_thread_context + self.save_thread_context = save_thread_context + self.next: Callable[[], None] = next self.next_: Callable[[], None] = next
            @@ -239,6 +261,10 @@

            Class variables

            fail() utility function, signal that the custom function failed to complete

            +
            var get_thread_context : Optional[GetThreadContext]
            +
            +

            get_thread_context() utility function for AI Agents & Assistants

            +
            var logger : logging.Logger

            Logger instance

            @@ -283,10 +309,26 @@

            Class variables

            Response representation

            +
            var save_thread_context : Optional[SaveThreadContext]
            +
            +

            save_thread_context() utility function for AI Agents & Assistants

            +
            var saySay

            say() utility function, which calls chat.postMessage API with the associated channel ID

            +
            var set_status : Optional[SetStatus]
            +
            +

            set_status() utility function for AI Agents & Assistants

            +
            +
            var set_suggested_prompts : Optional[SetSuggestedPrompts]
            +
            +

            set_suggested_prompts() utility function for AI Agents & Assistants

            +
            +
            var set_title : Optional[SetTitle]
            +
            +

            set_title() utility function for AI Agents & Assistants

            +
            var shortcut : Optional[Dict[str, Any]]

            An alias for payload in an @app.shortcut listener

            @@ -314,7 +356,7 @@

            Class variables

            • Args

              -
                + diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html b/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html index cf0dfc308..08aa83cbf 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html @@ -37,7 +37,7 @@

                Classes

                class AsyncArgs -(*, logger: logging.Logger, client: slack_sdk.web.async_client.AsyncWebClient, req: AsyncBoltRequest, resp: BoltResponse, context: AsyncBoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: AsyncAck, say: AsyncSay, respond: AsyncRespond, complete: AsyncComplete, fail: AsyncFail, next: Callable[[], Awaitable[None]], **kwargs) +(*, logger: logging.Logger, client: slack_sdk.web.async_client.AsyncWebClient, req: AsyncBoltRequest, resp: BoltResponse, context: AsyncBoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: AsyncAck, say: AsyncSay, respond: AsyncRespond, complete: AsyncComplete, fail: AsyncFail, set_status: Optional[AsyncSetStatus] = None, set_title: Optional[AsyncSetTitle] = None, set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] = None, get_thread_context: Optional[AsyncGetThreadContext] = None, save_thread_context: Optional[AsyncSaveThreadContext] = None, next: Callable[[], Awaitable[None]], **kwargs)

                All the arguments in this class are available in any middleware / listeners. @@ -143,6 +143,16 @@

                Classes

                """`complete()` utility function, signals a successful completion of the custom function""" fail: AsyncFail """`fail()` utility function, signal that the custom function failed to complete""" + set_status: Optional[AsyncSetStatus] + """`set_status()` utility function for AI Agents & Assistants""" + set_title: Optional[AsyncSetTitle] + """`set_title()` utility function for AI Agents & Assistants""" + set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] + """`set_suggested_prompts()` utility function for AI Agents & Assistants""" + get_thread_context: Optional[AsyncGetThreadContext] + """`get_thread_context()` utility function for AI Agents & Assistants""" + save_thread_context: Optional[AsyncSaveThreadContext] + """`save_thread_context()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], Awaitable[None]] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -171,8 +181,13 @@

                Classes

                respond: AsyncRespond, complete: AsyncComplete, fail: AsyncFail, + set_status: Optional[AsyncSetStatus] = None, + set_title: Optional[AsyncSetTitle] = None, + set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] = None, + get_thread_context: Optional[AsyncGetThreadContext] = None, + save_thread_context: Optional[AsyncSaveThreadContext] = None, next: Callable[[], Awaitable[None]], - **kwargs # noqa + **kwargs, # noqa ): self.logger: Logger = logger self.client: AsyncWebClient = client @@ -195,6 +210,13 @@

                Classes

                self.respond: AsyncRespond = respond self.complete: AsyncComplete = complete self.fail: AsyncFail = fail + + self.set_status = set_status + self.set_title = set_title + self.set_suggested_prompts = set_suggested_prompts + self.get_thread_context = get_thread_context + self.save_thread_context = save_thread_context + self.next: Callable[[], Awaitable[None]] = next self.next_: Callable[[], Awaitable[None]] = next @@ -236,6 +258,10 @@

                Class variables

                fail() utility function, signal that the custom function failed to complete

                +
                var get_thread_context : Optional[AsyncGetThreadContext]
                +
                +

                get_thread_context() utility function for AI Agents & Assistants

                +
                var logger : logging.Logger

                Logger instance

                @@ -280,10 +306,26 @@

                Class variables

                Response representation

                +
                var save_thread_context : Optional[AsyncSaveThreadContext]
                +
                +

                save_thread_context() utility function for AI Agents & Assistants

                +
                var sayAsyncSay

                say() utility function, which calls chat.postMessage API with the associated channel ID

                +
                var set_status : Optional[AsyncSetStatus]
                +
                +

                set_status() utility function for AI Agents & Assistants

                +
                +
                var set_suggested_prompts : Optional[AsyncSetSuggestedPrompts]
                +
                +

                set_suggested_prompts() utility function for AI Agents & Assistants

                +
                +
                var set_title : Optional[AsyncSetTitle]
                +
                +

                set_title() utility function for AI Agents & Assistants

                +
                var shortcut : Optional[Dict[str, Any]]

                An alias for payload in an @app.shortcut listener

                @@ -311,7 +353,7 @@

                Class variables

                • AsyncArgs

                  -
                    + diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/index.html b/docs/static/api-docs/slack_bolt/kwargs_injection/index.html index f44ca9c83..695b30653 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/index.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/index.html @@ -68,7 +68,7 @@

                    Classes

                    class Args -(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, next: Callable[[], None], **kwargs) +(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, next: Callable[[], None], **kwargs)

                    All the arguments in this class are available in any middleware / listeners. @@ -174,6 +174,16 @@

                    Classes

                    """`complete()` utility function, signals a successful completion of the custom function""" fail: Fail """`fail()` utility function, signal that the custom function failed to complete""" + set_status: Optional[SetStatus] + """`set_status()` utility function for AI Agents & Assistants""" + set_title: Optional[SetTitle] + """`set_title()` utility function for AI Agents & Assistants""" + set_suggested_prompts: Optional[SetSuggestedPrompts] + """`set_suggested_prompts()` utility function for AI Agents & Assistants""" + get_thread_context: Optional[GetThreadContext] + """`get_thread_context()` utility function for AI Agents & Assistants""" + save_thread_context: Optional[SaveThreadContext] + """`save_thread_context()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -202,11 +212,16 @@

                    Classes

                    respond: Respond, complete: Complete, fail: Fail, + set_status: Optional[SetStatus] = None, + set_title: Optional[SetTitle] = None, + set_suggested_prompts: Optional[SetSuggestedPrompts] = None, + get_thread_context: Optional[GetThreadContext] = None, + save_thread_context: Optional[SaveThreadContext] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], None], - **kwargs # noqa + **kwargs, # noqa ): self.logger: logging.Logger = logger self.client: WebClient = client @@ -229,6 +244,13 @@

                    Classes

                    self.respond: Respond = respond self.complete: Complete = complete self.fail: Fail = fail + + self.set_status = set_status + self.set_title = set_title + self.set_suggested_prompts = set_suggested_prompts + self.get_thread_context = get_thread_context + self.save_thread_context = save_thread_context + self.next: Callable[[], None] = next self.next_: Callable[[], None] = next @@ -270,6 +292,10 @@

                    Class variables

                    fail() utility function, signal that the custom function failed to complete

                    +
                    var get_thread_context : Optional[GetThreadContext]
                    +
                    +

                    get_thread_context() utility function for AI Agents & Assistants

                    +
                    var logger : logging.Logger

                    Logger instance

                    @@ -314,10 +340,26 @@

                    Class variables

                    Response representation

                    +
                    var save_thread_context : Optional[SaveThreadContext]
                    +
                    +

                    save_thread_context() utility function for AI Agents & Assistants

                    +
                    var saySay

                    say() utility function, which calls chat.postMessage API with the associated channel ID

                    +
                    var set_status : Optional[SetStatus]
                    +
                    +

                    set_status() utility function for AI Agents & Assistants

                    +
                    +
                    var set_suggested_prompts : Optional[SetSuggestedPrompts]
                    +
                    +

                    set_suggested_prompts() utility function for AI Agents & Assistants

                    +
                    +
                    var set_title : Optional[SetTitle]
                    +
                    +

                    set_title() utility function for AI Agents & Assistants

                    +
                    var shortcut : Optional[Dict[str, Any]]

                    An alias for payload in an @app.shortcut listener

                    @@ -358,7 +400,7 @@

                    Class variables

                    • Args

                      -
                        + diff --git a/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html b/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html index a98c3fe3f..389fbb824 100644 --- a/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html +++ b/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html @@ -195,12 +195,15 @@

                        Classes

                        copied_request = self._build_lazy_request(request, func_name) self.lazy_listener_runner.start(function=lazy_func, request=copied_request) - @staticmethod - def _build_lazy_request(request: AsyncBoltRequest, lazy_func_name: str) -> AsyncBoltRequest: - copied_request = create_copy(request.to_copyable()) - copied_request.method = "NONE" + def _build_lazy_request(self, request: AsyncBoltRequest, lazy_func_name: str) -> AsyncBoltRequest: + copied_request: AsyncBoltRequest = create_copy(request.to_copyable()) copied_request.lazy_only = True copied_request.lazy_function_name = lazy_func_name + copied_request.context["listener_runner"] = self + if request.context.get_thread_context is not None: + copied_request.context["get_thread_context"] = request.context.get_thread_context + if request.context.save_thread_context is not None: + copied_request.context["save_thread_context"] = request.context.save_thread_context return copied_request def _debug_log_completion(self, starting_time: float, response: BoltResponse) -> None: diff --git a/docs/static/api-docs/slack_bolt/listener/thread_runner.html b/docs/static/api-docs/slack_bolt/listener/thread_runner.html index 7b9ae9f2b..978561628 100644 --- a/docs/static/api-docs/slack_bolt/listener/thread_runner.html +++ b/docs/static/api-docs/slack_bolt/listener/thread_runner.html @@ -212,12 +212,16 @@

                        Classes

                        copied_request = self._build_lazy_request(request, func_name) self.lazy_listener_runner.start(function=lazy_func, request=copied_request) - @staticmethod - def _build_lazy_request(request: BoltRequest, lazy_func_name: str) -> BoltRequest: - copied_request = create_copy(request.to_copyable()) - copied_request.method = "NONE" + def _build_lazy_request(self, request: BoltRequest, lazy_func_name: str) -> BoltRequest: + copied_request: BoltRequest = create_copy(request.to_copyable()) copied_request.lazy_only = True copied_request.lazy_function_name = lazy_func_name + # These are not copyable objects, so manually set for a different thread + copied_request.context["listener_runner"] = self + if request.context.get_thread_context is not None: + copied_request.context["get_thread_context"] = request.context.get_thread_context + if request.context.save_thread_context is not None: + copied_request.context["save_thread_context"] = request.context.save_thread_context return copied_request def _debug_log_completion(self, starting_time: float, response: BoltResponse) -> None: diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html b/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html new file mode 100644 index 000000000..31ecabdb1 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html @@ -0,0 +1,416 @@ + + + + + + +slack_bolt.middleware.assistant.assistant API documentation + + + + + + + + + + + +
                        +
                        +
                        +

                        Module slack_bolt.middleware.assistant.assistant

                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +

                        Classes

                        +
                        +
                        +class Assistant +(*, app_name: str = 'assistant', thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +
                        +
                        +

                        A middleware can process request data before other middleware and listener functions.

                        +
                        + +Expand source code + +
                        class Assistant(Middleware):
                        +    _thread_started_listeners: Optional[List[Listener]]
                        +    _thread_context_changed_listeners: Optional[List[Listener]]
                        +    _user_message_listeners: Optional[List[Listener]]
                        +    _bot_message_listeners: Optional[List[Listener]]
                        +
                        +    thread_context_store: Optional[AssistantThreadContextStore]
                        +    base_logger: Optional[logging.Logger]
                        +
                        +    def __init__(
                        +        self,
                        +        *,
                        +        app_name: str = "assistant",
                        +        thread_context_store: Optional[AssistantThreadContextStore] = None,
                        +        logger: Optional[logging.Logger] = None,
                        +    ):
                        +        self.app_name = app_name
                        +        self.thread_context_store = thread_context_store
                        +        self.base_logger = logger
                        +
                        +        self._thread_started_listeners = None
                        +        self._thread_context_changed_listeners = None
                        +        self._user_message_listeners = None
                        +        self._bot_message_listeners = None
                        +
                        +    def thread_started(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._thread_started_listeners is None:
                        +            self._thread_started_listeners = []
                        +        all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._thread_started_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._thread_started_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def user_message(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._user_message_listeners is None:
                        +            self._user_message_listeners = []
                        +        all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._user_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._user_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def bot_message(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._bot_message_listeners is None:
                        +            self._bot_message_listeners = []
                        +        all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._bot_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._bot_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def thread_context_changed(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._thread_context_changed_listeners is None:
                        +            self._thread_context_changed_listeners = []
                        +        all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._thread_context_changed_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._thread_context_changed_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def _merge_matchers(
                        +        self,
                        +        primary_matcher: Callable[..., bool],
                        +        custom_matchers: Optional[Union[Callable[..., bool], ListenerMatcher]],
                        +    ):
                        +        return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + (
                        +            custom_matchers or []
                        +        )  # type:ignore[operator]
                        +
                        +    @staticmethod
                        +    def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict):
                        +        save_thread_context(payload["assistant_thread"]["context"])
                        +
                        +    def process(  # type:ignore[return]
                        +        self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]
                        +    ) -> Optional[BoltResponse]:
                        +        if self._thread_context_changed_listeners is None:
                        +            self.thread_context_changed(self.default_thread_context_changed)
                        +
                        +        listener_runner: ThreadListenerRunner = req.context.listener_runner
                        +        for listeners in [
                        +            self._thread_started_listeners,
                        +            self._thread_context_changed_listeners,
                        +            self._user_message_listeners,
                        +            self._bot_message_listeners,
                        +        ]:
                        +            if listeners is not None:
                        +                for listener in listeners:
                        +                    if listener.matches(req=req, resp=resp):
                        +                        return listener_runner.run(
                        +                            request=req,
                        +                            response=resp,
                        +                            listener_name="assistant_listener",
                        +                            listener=listener,
                        +                        )
                        +        if is_other_message_sub_event_in_assistant_thread(req.body):
                        +            # message_changed, message_deleted, etc.
                        +            return req.context.ack()
                        +
                        +        next()
                        +
                        +    def build_listener(
                        +        self,
                        +        listener_or_functions: Union[Listener, Callable, List[Callable]],
                        +        matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None,
                        +        middleware: Optional[List[Middleware]] = None,
                        +        base_logger: Optional[Logger] = None,
                        +    ) -> Listener:
                        +        if isinstance(listener_or_functions, Callable):  # type:ignore[arg-type]
                        +            listener_or_functions = [listener_or_functions]  # type:ignore[list-item]
                        +
                        +        if isinstance(listener_or_functions, Listener):
                        +            return listener_or_functions
                        +        elif isinstance(listener_or_functions, list):
                        +            middleware = middleware if middleware else []
                        +            functions = listener_or_functions
                        +            ack_function = functions.pop(0)
                        +
                        +            matchers = matchers if matchers else []
                        +            listener_matchers: List[ListenerMatcher] = []
                        +            for matcher in matchers:
                        +                if isinstance(matcher, ListenerMatcher):
                        +                    listener_matchers.append(matcher)
                        +                elif isinstance(matcher, Callable):  # type:ignore[arg-type]
                        +                    listener_matchers.append(
                        +                        build_listener_matcher(
                        +                            func=matcher,
                        +                            asyncio=False,
                        +                            base_logger=base_logger,
                        +                        )
                        +                    )
                        +            return CustomListener(
                        +                app_name=self.app_name,
                        +                matchers=listener_matchers,
                        +                middleware=middleware,
                        +                ack_function=ack_function,
                        +                lazy_functions=functions,
                        +                auto_acknowledgement=True,
                        +                base_logger=base_logger or self.base_logger,
                        +            )
                        +        else:
                        +            raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected")
                        +
                        +

                        Ancestors

                        + +

                        Class variables

                        +
                        +
                        var base_logger : Optional[logging.Logger]
                        +
                        +
                        +
                        +
                        var thread_context_store : Optional[AssistantThreadContextStore]
                        +
                        +
                        +
                        +
                        +

                        Static methods

                        +
                        +
                        +def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict) +
                        +
                        +
                        +
                        +
                        +

                        Methods

                        +
                        +
                        +def bot_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def build_listener(self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> Listener +
                        +
                        +
                        +
                        +
                        +def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def thread_started(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def user_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +

                        Inherited members

                        + +
                        +
                        +
                        +
                        + +
                        + + + diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html b/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html new file mode 100644 index 000000000..bf4ec0ce8 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html @@ -0,0 +1,447 @@ + + + + + + +slack_bolt.middleware.assistant.async_assistant API documentation + + + + + + + + + + + +
                        +
                        +
                        +

                        Module slack_bolt.middleware.assistant.async_assistant

                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +

                        Classes

                        +
                        +
                        +class AsyncAssistant +(*, app_name: str = 'assistant', thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +
                        +
                        +

                        A middleware can process request data before other middleware and listener functions.

                        +
                        + +Expand source code + +
                        class AsyncAssistant(AsyncMiddleware):
                        +    _thread_started_listeners: Optional[List[AsyncListener]]
                        +    _user_message_listeners: Optional[List[AsyncListener]]
                        +    _bot_message_listeners: Optional[List[AsyncListener]]
                        +    _thread_context_changed_listeners: Optional[List[AsyncListener]]
                        +
                        +    thread_context_store: Optional[AsyncAssistantThreadContextStore]
                        +    base_logger: Optional[logging.Logger]
                        +
                        +    def __init__(
                        +        self,
                        +        *,
                        +        app_name: str = "assistant",
                        +        thread_context_store: Optional[AsyncAssistantThreadContextStore] = None,
                        +        logger: Optional[logging.Logger] = None,
                        +    ):
                        +        self.app_name = app_name
                        +        self.thread_context_store = thread_context_store
                        +        self.base_logger = logger
                        +
                        +        self._thread_started_listeners = None
                        +        self._thread_context_changed_listeners = None
                        +        self._user_message_listeners = None
                        +        self._bot_message_listeners = None
                        +
                        +    def thread_started(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._thread_started_listeners is None:
                        +            self._thread_started_listeners = []
                        +        all_matchers = self._merge_matchers(
                        +            build_listener_matcher(
                        +                func=is_assistant_thread_started_event,
                        +                asyncio=True,
                        +                base_logger=self.base_logger,
                        +            ),  # type:ignore[arg-type]
                        +            matchers,
                        +        )
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._thread_started_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._thread_started_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def user_message(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._user_message_listeners is None:
                        +            self._user_message_listeners = []
                        +        all_matchers = self._merge_matchers(
                        +            build_listener_matcher(
                        +                func=is_user_message_event_in_assistant_thread,
                        +                asyncio=True,
                        +                base_logger=self.base_logger,
                        +            ),  # type:ignore[arg-type]
                        +            matchers,
                        +        )
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._user_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._user_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def bot_message(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._bot_message_listeners is None:
                        +            self._bot_message_listeners = []
                        +        all_matchers = self._merge_matchers(
                        +            build_listener_matcher(
                        +                func=is_bot_message_event_in_assistant_thread,
                        +                asyncio=True,
                        +                base_logger=self.base_logger,
                        +            ),  # type:ignore[arg-type]
                        +            matchers,
                        +        )
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._bot_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._bot_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def thread_context_changed(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, AsyncMiddleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._thread_context_changed_listeners is None:
                        +            self._thread_context_changed_listeners = []
                        +        all_matchers = self._merge_matchers(
                        +            build_listener_matcher(
                        +                func=is_assistant_thread_context_changed_event,
                        +                asyncio=True,
                        +                base_logger=self.base_logger,
                        +            ),  # type:ignore[arg-type]
                        +            matchers,
                        +        )
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._thread_context_changed_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._thread_context_changed_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    @staticmethod
                        +    def _merge_matchers(
                        +        primary_matcher: Union[Callable[..., bool], AsyncListenerMatcher],
                        +        custom_matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]],
                        +    ):
                        +        return [primary_matcher] + (custom_matchers or [])  # type:ignore[operator]
                        +
                        +    @staticmethod
                        +    async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict):
                        +        new_context: dict = payload["assistant_thread"]["context"]
                        +        await save_thread_context(new_context)
                        +
                        +    async def async_process(  # type:ignore[return]
                        +        self,
                        +        *,
                        +        req: AsyncBoltRequest,
                        +        resp: BoltResponse,
                        +        next: Callable[[], Awaitable[BoltResponse]],
                        +    ) -> Optional[BoltResponse]:
                        +        if self._thread_context_changed_listeners is None:
                        +            self.thread_context_changed(self.default_thread_context_changed)
                        +
                        +        listener_runner: AsyncioListenerRunner = req.context.listener_runner
                        +        for listeners in [
                        +            self._thread_started_listeners,
                        +            self._thread_context_changed_listeners,
                        +            self._user_message_listeners,
                        +            self._bot_message_listeners,
                        +        ]:
                        +            if listeners is not None:
                        +                for listener in listeners:
                        +                    if listener is not None and await listener.async_matches(req=req, resp=resp):
                        +                        return await listener_runner.run(
                        +                            request=req,
                        +                            response=resp,
                        +                            listener_name="assistant_listener",
                        +                            listener=listener,
                        +                        )
                        +        if is_other_message_sub_event_in_assistant_thread(req.body):
                        +            # message_changed, message_deleted, etc.
                        +            return await req.context.ack()
                        +
                        +        await next()
                        +
                        +    def build_listener(
                        +        self,
                        +        listener_or_functions: Union[AsyncListener, Callable, List[Callable]],
                        +        matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]] = None,
                        +        middleware: Optional[List[AsyncMiddleware]] = None,
                        +        base_logger: Optional[Logger] = None,
                        +    ) -> AsyncListener:
                        +        if isinstance(listener_or_functions, Callable):  # type:ignore[arg-type]
                        +            listener_or_functions = [listener_or_functions]  # type:ignore[list-item]
                        +
                        +        if isinstance(listener_or_functions, AsyncListener):
                        +            return listener_or_functions
                        +        elif isinstance(listener_or_functions, list):
                        +            middleware = middleware if middleware else []
                        +            functions = listener_or_functions
                        +            ack_function = functions.pop(0)
                        +
                        +            matchers = matchers if matchers else []
                        +            listener_matchers: List[AsyncListenerMatcher] = []
                        +            for matcher in matchers:
                        +                if isinstance(matcher, AsyncListenerMatcher):
                        +                    listener_matchers.append(matcher)
                        +                else:
                        +                    listener_matchers.append(
                        +                        build_listener_matcher(
                        +                            func=matcher,  # type:ignore[arg-type]
                        +                            asyncio=True,
                        +                            base_logger=base_logger,
                        +                        )
                        +                    )
                        +            return AsyncCustomListener(
                        +                app_name=self.app_name,
                        +                matchers=listener_matchers,
                        +                middleware=middleware,
                        +                ack_function=ack_function,
                        +                lazy_functions=functions,
                        +                auto_acknowledgement=True,
                        +                base_logger=base_logger or self.base_logger,
                        +            )
                        +        else:
                        +            raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected")
                        +
                        +

                        Ancestors

                        + +

                        Class variables

                        +
                        +
                        var base_logger : Optional[logging.Logger]
                        +
                        +
                        +
                        +
                        var thread_context_store : Optional[AsyncAssistantThreadContextStore]
                        +
                        +
                        +
                        +
                        +

                        Static methods

                        +
                        +
                        +async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict) +
                        +
                        +
                        +
                        +
                        +

                        Methods

                        +
                        +
                        +def bot_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def build_listener(self, listener_or_functions: Union[AsyncListener, Callable, List[Callable]], matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]] = None, middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> AsyncListener +
                        +
                        +
                        +
                        +
                        +def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def thread_started(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def user_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +

                        Inherited members

                        + +
                        +
                        +
                        +
                        + +
                        + + + diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/index.html b/docs/static/api-docs/slack_bolt/middleware/assistant/index.html new file mode 100644 index 000000000..342b26182 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/middleware/assistant/index.html @@ -0,0 +1,433 @@ + + + + + + +slack_bolt.middleware.assistant API documentation + + + + + + + + + + + +
                        +
                        +
                        +

                        Module slack_bolt.middleware.assistant

                        +
                        +
                        +
                        +
                        +

                        Sub-modules

                        +
                        +
                        slack_bolt.middleware.assistant.assistant
                        +
                        +
                        +
                        +
                        slack_bolt.middleware.assistant.async_assistant
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +
                        +

                        Classes

                        +
                        +
                        +class Assistant +(*, app_name: str = 'assistant', thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +
                        +
                        +

                        A middleware can process request data before other middleware and listener functions.

                        +
                        + +Expand source code + +
                        class Assistant(Middleware):
                        +    _thread_started_listeners: Optional[List[Listener]]
                        +    _thread_context_changed_listeners: Optional[List[Listener]]
                        +    _user_message_listeners: Optional[List[Listener]]
                        +    _bot_message_listeners: Optional[List[Listener]]
                        +
                        +    thread_context_store: Optional[AssistantThreadContextStore]
                        +    base_logger: Optional[logging.Logger]
                        +
                        +    def __init__(
                        +        self,
                        +        *,
                        +        app_name: str = "assistant",
                        +        thread_context_store: Optional[AssistantThreadContextStore] = None,
                        +        logger: Optional[logging.Logger] = None,
                        +    ):
                        +        self.app_name = app_name
                        +        self.thread_context_store = thread_context_store
                        +        self.base_logger = logger
                        +
                        +        self._thread_started_listeners = None
                        +        self._thread_context_changed_listeners = None
                        +        self._user_message_listeners = None
                        +        self._bot_message_listeners = None
                        +
                        +    def thread_started(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._thread_started_listeners is None:
                        +            self._thread_started_listeners = []
                        +        all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._thread_started_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._thread_started_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def user_message(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._user_message_listeners is None:
                        +            self._user_message_listeners = []
                        +        all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._user_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._user_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def bot_message(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._bot_message_listeners is None:
                        +            self._bot_message_listeners = []
                        +        all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._bot_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._bot_message_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def thread_context_changed(
                        +        self,
                        +        *args,
                        +        matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None,
                        +        middleware: Optional[Union[Callable, Middleware]] = None,
                        +        lazy: Optional[List[Callable[..., None]]] = None,
                        +    ):
                        +        if self._thread_context_changed_listeners is None:
                        +            self._thread_context_changed_listeners = []
                        +        all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers)
                        +        if is_used_without_argument(args):
                        +            func = args[0]
                        +            self._thread_context_changed_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=func,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,  # type:ignore[arg-type]
                        +                )
                        +            )
                        +            return func
                        +
                        +        def _inner(func):
                        +            functions = [func] + (lazy if lazy is not None else [])
                        +            self._thread_context_changed_listeners.append(
                        +                self.build_listener(
                        +                    listener_or_functions=functions,
                        +                    matchers=all_matchers,
                        +                    middleware=middleware,
                        +                )
                        +            )
                        +
                        +            @wraps(func)
                        +            def _wrapper(*args, **kwargs):
                        +                return func(*args, **kwargs)
                        +
                        +            return _wrapper
                        +
                        +        return _inner
                        +
                        +    def _merge_matchers(
                        +        self,
                        +        primary_matcher: Callable[..., bool],
                        +        custom_matchers: Optional[Union[Callable[..., bool], ListenerMatcher]],
                        +    ):
                        +        return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + (
                        +            custom_matchers or []
                        +        )  # type:ignore[operator]
                        +
                        +    @staticmethod
                        +    def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict):
                        +        save_thread_context(payload["assistant_thread"]["context"])
                        +
                        +    def process(  # type:ignore[return]
                        +        self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]
                        +    ) -> Optional[BoltResponse]:
                        +        if self._thread_context_changed_listeners is None:
                        +            self.thread_context_changed(self.default_thread_context_changed)
                        +
                        +        listener_runner: ThreadListenerRunner = req.context.listener_runner
                        +        for listeners in [
                        +            self._thread_started_listeners,
                        +            self._thread_context_changed_listeners,
                        +            self._user_message_listeners,
                        +            self._bot_message_listeners,
                        +        ]:
                        +            if listeners is not None:
                        +                for listener in listeners:
                        +                    if listener.matches(req=req, resp=resp):
                        +                        return listener_runner.run(
                        +                            request=req,
                        +                            response=resp,
                        +                            listener_name="assistant_listener",
                        +                            listener=listener,
                        +                        )
                        +        if is_other_message_sub_event_in_assistant_thread(req.body):
                        +            # message_changed, message_deleted, etc.
                        +            return req.context.ack()
                        +
                        +        next()
                        +
                        +    def build_listener(
                        +        self,
                        +        listener_or_functions: Union[Listener, Callable, List[Callable]],
                        +        matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None,
                        +        middleware: Optional[List[Middleware]] = None,
                        +        base_logger: Optional[Logger] = None,
                        +    ) -> Listener:
                        +        if isinstance(listener_or_functions, Callable):  # type:ignore[arg-type]
                        +            listener_or_functions = [listener_or_functions]  # type:ignore[list-item]
                        +
                        +        if isinstance(listener_or_functions, Listener):
                        +            return listener_or_functions
                        +        elif isinstance(listener_or_functions, list):
                        +            middleware = middleware if middleware else []
                        +            functions = listener_or_functions
                        +            ack_function = functions.pop(0)
                        +
                        +            matchers = matchers if matchers else []
                        +            listener_matchers: List[ListenerMatcher] = []
                        +            for matcher in matchers:
                        +                if isinstance(matcher, ListenerMatcher):
                        +                    listener_matchers.append(matcher)
                        +                elif isinstance(matcher, Callable):  # type:ignore[arg-type]
                        +                    listener_matchers.append(
                        +                        build_listener_matcher(
                        +                            func=matcher,
                        +                            asyncio=False,
                        +                            base_logger=base_logger,
                        +                        )
                        +                    )
                        +            return CustomListener(
                        +                app_name=self.app_name,
                        +                matchers=listener_matchers,
                        +                middleware=middleware,
                        +                ack_function=ack_function,
                        +                lazy_functions=functions,
                        +                auto_acknowledgement=True,
                        +                base_logger=base_logger or self.base_logger,
                        +            )
                        +        else:
                        +            raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected")
                        +
                        +

                        Ancestors

                        + +

                        Class variables

                        +
                        +
                        var base_logger : Optional[logging.Logger]
                        +
                        +
                        +
                        +
                        var thread_context_store : Optional[AssistantThreadContextStore]
                        +
                        +
                        +
                        +
                        +

                        Static methods

                        +
                        +
                        +def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict) +
                        +
                        +
                        +
                        +
                        +

                        Methods

                        +
                        +
                        +def bot_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def build_listener(self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> Listener +
                        +
                        +
                        +
                        +
                        +def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def thread_started(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +def user_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +
                        +
                        +
                        +
                        +
                        +

                        Inherited members

                        + +
                        +
                        +
                        +
                        + +
                        + + + diff --git a/docs/static/api-docs/slack_bolt/middleware/async_builtins.html b/docs/static/api-docs/slack_bolt/middleware/async_builtins.html index 12ac1b808..5ef498865 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_builtins.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_builtins.html @@ -54,7 +54,7 @@

                        Classes

                        next: Callable[[], Awaitable[BoltResponse]], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] + req.context.client.token = req.context.function_bot_access_token return await next()
                        @@ -74,7 +74,7 @@

                        Inherited members

                    class AsyncIgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None) +(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True)

                    A middleware can process request data before other middleware and listener functions.

                    @@ -95,6 +95,11 @@

                    Inherited members

                    # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] + if self.ignoring_self_assistant_message_events_enabled is False: + if is_bot_message_event_in_assistant_thread(req.body): + # Assistant#bot_message handler acknowledges this pattern + return await next() + self._debug_log(req.body) return await req.context.ack() else: @@ -291,6 +296,17 @@

                    Ancestors

                  • Middleware
                  • AsyncMiddleware
                  +

                  Class variables

                  +
                  +
                  var logger : logging.Logger
                  +
                  +
                  +
                  +
                  var verification_token : Optional[str]
                  +
                  +
                  +
                  +

                  Inherited members

                  • SslCheck: @@ -389,6 +405,10 @@

                    AsyncSslCheck

                    +
                  • AsyncUrlVerification

                    diff --git a/docs/static/api-docs/slack_bolt/middleware/async_middleware.html b/docs/static/api-docs/slack_bolt/middleware/async_middleware.html index f7189ad2b..13704234c 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_middleware.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_middleware.html @@ -91,6 +91,7 @@

                    Classes

                    Subclasses

                      +
                    • AsyncAssistant
                    • AsyncCustomMiddleware
                    • AsyncAttachingFunctionToken
                    • AsyncAuthorization
                    • diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html index 0ac3072f7..068be8cb2 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html @@ -54,7 +54,7 @@

                      Classes

                      next: Callable[[], Awaitable[BoltResponse]], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] + req.context.client.token = req.context.function_bot_access_token return await next() diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html index 8f501d9f2..bf323aead 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html @@ -54,7 +54,7 @@

                      Classes

                      next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] + req.context.client.token = req.context.function_bot_access_token return next() diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html index 06ba97ee7..0ddd5b07f 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html @@ -65,7 +65,7 @@

                      Classes

                      next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] + req.context.client.token = req.context.function_bot_access_token return next() diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html index 7ec390f38..4f8be284d 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html @@ -131,7 +131,7 @@

                      Args

                      req.context["token"] = token # As AsyncApp#_init_context() generates a new AsyncWebClient for this request, # it's safe to modify this instance. - req.context.client.token = token # type: ignore[union-attr] + req.context.client.token = token return await next() else: # This situation can arise if: diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html index 8179b6c29..aaa88cf6c 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html @@ -84,13 +84,13 @@

                      Classes

                      try: if self.auth_test_result is None: - self.auth_test_result = await req.context.client.auth_test() # type: ignore[union-attr] + self.auth_test_result = await req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, # type: ignore[union-attr] + token=req.context.client.token, request_user_id=req.context.user_id, ) ) diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/index.html b/docs/static/api-docs/slack_bolt/middleware/authorization/index.html index d72ad72c9..9430bb94e 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/index.html @@ -195,7 +195,7 @@

                      Args

                      req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. - req.context.client.token = token # type: ignore[union-attr] + req.context.client.token = token return next() else: # This situation can arise if: @@ -288,6 +288,7 @@

                      Args

                      # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: + if _is_no_auth_required(req): return next() @@ -303,13 +304,13 @@

                      Args

                      try: if not self.auth_test_result: - self.auth_test_result = req.context.client.auth_test() # type: ignore[union-attr] + self.auth_test_result = req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, # type: ignore[union-attr] + token=req.context.client.token, request_user_id=req.context.user_id, ) ) diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html index f5400a4d4..722d89371 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html @@ -129,7 +129,7 @@

                      Args

                      req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. - req.context.client.token = token # type: ignore[union-attr] + req.context.client.token = token return next() else: # This situation can arise if: diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html index 29dc60414..a2ae9c009 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html @@ -83,6 +83,7 @@

                      Args

                      # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: + if _is_no_auth_required(req): return next() @@ -98,13 +99,13 @@

                      Args

                      try: if not self.auth_test_result: - self.auth_test_result = req.context.client.auth_test() # type: ignore[union-attr] + self.auth_test_result = req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, # type: ignore[union-attr] + token=req.context.client.token, request_user_id=req.context.user_id, ) ) diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html index d02a44677..0d549afc7 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html @@ -37,7 +37,7 @@

                      Classes

                      class AsyncIgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None) +(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True)

                      A middleware can process request data before other middleware and listener functions.

                      @@ -58,6 +58,11 @@

                      Classes

                      # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] + if self.ignoring_self_assistant_message_events_enabled is False: + if is_bot_message_event_in_assistant_thread(req.body): + # Assistant#bot_message handler acknowledges this pattern + return await next() + self._debug_log(req.body) return await req.context.ack() else: diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html index e5e9222d6..e22295094 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html @@ -37,7 +37,7 @@

                      Classes

                      class IgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None) +(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True)

                      A middleware can process request data before other middleware and listener functions.

                      @@ -47,9 +47,14 @@

                      Classes

                      Expand source code
                      class IgnoringSelfEvents(Middleware):
                      -    def __init__(self, base_logger: Optional[logging.Logger] = None):
                      +    def __init__(
                      +        self,
                      +        base_logger: Optional[logging.Logger] = None,
                      +        ignoring_self_assistant_message_events_enabled: bool = True,
                      +    ):
                               """Ignores the events generated by this bot user itself."""
                               self.logger = get_bolt_logger(IgnoringSelfEvents, base_logger=base_logger)
                      +        self.ignoring_self_assistant_message_events_enabled = ignoring_self_assistant_message_events_enabled
                       
                           def process(
                               self,
                      @@ -62,6 +67,11 @@ 

                      Classes

                      # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] + if self.ignoring_self_assistant_message_events_enabled is False: + if is_bot_message_event_in_assistant_thread(req.body): + # Assistant#bot_message handler acknowledges this pattern + return next() + self._debug_log(req.body) return req.context.ack() else: diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html index 68eef6bfd..853d1225c 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html @@ -48,7 +48,7 @@

                      Classes

                      class IgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None) +(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True)

                      A middleware can process request data before other middleware and listener functions.

                      @@ -58,9 +58,14 @@

                      Classes

                      Expand source code
                      class IgnoringSelfEvents(Middleware):
                      -    def __init__(self, base_logger: Optional[logging.Logger] = None):
                      +    def __init__(
                      +        self,
                      +        base_logger: Optional[logging.Logger] = None,
                      +        ignoring_self_assistant_message_events_enabled: bool = True,
                      +    ):
                               """Ignores the events generated by this bot user itself."""
                               self.logger = get_bolt_logger(IgnoringSelfEvents, base_logger=base_logger)
                      +        self.ignoring_self_assistant_message_events_enabled = ignoring_self_assistant_message_events_enabled
                       
                           def process(
                               self,
                      @@ -73,6 +78,11 @@ 

                      Classes

                      # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] + if self.ignoring_self_assistant_message_events_enabled is False: + if is_bot_message_event_in_assistant_thread(req.body): + # Assistant#bot_message handler acknowledges this pattern + return next() + self._debug_log(req.body) return req.context.ack() else: diff --git a/docs/static/api-docs/slack_bolt/middleware/index.html b/docs/static/api-docs/slack_bolt/middleware/index.html index 59cf0b3fc..2be166678 100644 --- a/docs/static/api-docs/slack_bolt/middleware/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/index.html @@ -34,6 +34,10 @@

                      Module slack_bolt.middleware

                      Sub-modules

                      +
                      slack_bolt.middleware.assistant
                      +
                      +
                      +
                      slack_bolt.middleware.async_builtins
                      @@ -118,7 +122,7 @@

                      Classes

                      next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: - req.context.client.token = req.context.function_bot_access_token # type: ignore[union-attr] + req.context.client.token = req.context.function_bot_access_token return next()
                      @@ -218,7 +222,7 @@

                      Inherited members

                      class IgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None) +(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True)

                      A middleware can process request data before other middleware and listener functions.

                      @@ -228,9 +232,14 @@

                      Inherited members

                      Expand source code
                      class IgnoringSelfEvents(Middleware):
                      -    def __init__(self, base_logger: Optional[logging.Logger] = None):
                      +    def __init__(
                      +        self,
                      +        base_logger: Optional[logging.Logger] = None,
                      +        ignoring_self_assistant_message_events_enabled: bool = True,
                      +    ):
                               """Ignores the events generated by this bot user itself."""
                               self.logger = get_bolt_logger(IgnoringSelfEvents, base_logger=base_logger)
                      +        self.ignoring_self_assistant_message_events_enabled = ignoring_self_assistant_message_events_enabled
                       
                           def process(
                               self,
                      @@ -243,6 +252,11 @@ 

                      Inherited members

                      # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] + if self.ignoring_self_assistant_message_events_enabled is False: + if is_bot_message_event_in_assistant_thread(req.body): + # Assistant#bot_message handler acknowledges this pattern + return next() + self._debug_log(req.body) return req.context.ack() else: @@ -359,6 +373,7 @@

                      Inherited members

                      Subclasses

                        +
                      • Assistant
                      • AttachingFunctionToken
                      • Authorization
                      • CustomMiddleware
                      • @@ -513,7 +528,7 @@

                        Args

                        req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. - req.context.client.token = token # type: ignore[union-attr] + req.context.client.token = token return next() else: # This situation can arise if: @@ -695,6 +710,7 @@

                        Args

                        # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: + if _is_no_auth_required(req): return next() @@ -710,13 +726,13 @@

                        Args

                        try: if not self.auth_test_result: - self.auth_test_result = req.context.client.auth_test() # type: ignore[union-attr] + self.auth_test_result = req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, - token=req.context.client.token, # type: ignore[union-attr] + token=req.context.client.token, request_user_id=req.context.user_id, ) ) @@ -936,6 +952,7 @@

                        Inherited members

                      • Sub-modules

                      @@ -113,6 +126,7 @@

                      Returns

                    • get_boot_message
                    • get_name_for_callable
                    • is_callable_coroutine
                    • +
                    • is_used_without_argument
                  diff --git a/docs/static/api-docs/slack_bolt/workflows/step/async_step.html b/docs/static/api-docs/slack_bolt/workflows/step/async_step.html index 0969b2461..b957cabad 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/async_step.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/async_step.html @@ -583,7 +583,7 @@

                  Class variables

                  Static methods

                  -def to_listener_matchers(app_name: str, matchers: Optional[List[Union[Callable[..., Awaitable[bool]], AsyncListenerMatcher]]]) ‑> List[AsyncListenerMatcher] +def to_listener_matchers(app_name: str, matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]]) ‑> List[AsyncListenerMatcher]
                  diff --git a/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html b/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html index 2b324a7b6..43034c84f 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html @@ -37,7 +37,7 @@

                  Classes

                  class AsyncWorkflowStepMiddleware -(step: AsyncWorkflowStep, listener_runner: AsyncioListenerRunner) +(step: AsyncWorkflowStep)

                  Base middleware for step from app specific ones

                  @@ -48,9 +48,8 @@

                  Classes

                  class AsyncWorkflowStepMiddleware(AsyncMiddleware):
                       """Base middleware for step from app specific ones"""
                   
                  -    def __init__(self, step: AsyncWorkflowStep, listener_runner: AsyncioListenerRunner):
                  +    def __init__(self, step: AsyncWorkflowStep):
                           self.step = step
                  -        self.listener_runner = listener_runner
                   
                       async def async_process(
                           self,
                  @@ -75,8 +74,8 @@ 

                  Classes

                  return await next() + @staticmethod async def _run( - self, listener: AsyncListener, req: AsyncBoltRequest, resp: BoltResponse, @@ -85,7 +84,7 @@

                  Classes

                  if next_was_not_called: return None - return await self.listener_runner.run( + return await req.context.listener_runner.run( request=req, response=resp, listener_name=get_name_for_callable(listener.ack_function), diff --git a/docs/static/api-docs/slack_bolt/workflows/step/index.html b/docs/static/api-docs/slack_bolt/workflows/step/index.html index b184a9a2e..17362d7b0 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/index.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/index.html @@ -593,7 +593,7 @@

                  Static methods

                  class WorkflowStepMiddleware -(step: WorkflowStep, listener_runner: ThreadListenerRunner) +(step: WorkflowStep)

                  Base middleware for step from app specific ones

                  @@ -604,9 +604,8 @@

                  Static methods

                  class WorkflowStepMiddleware(Middleware):
                       """Base middleware for step from app specific ones"""
                   
                  -    def __init__(self, step: WorkflowStep, listener_runner: ThreadListenerRunner):
                  +    def __init__(self, step: WorkflowStep):
                           self.step = step
                  -        self.listener_runner = listener_runner
                   
                       def process(
                           self,
                  @@ -634,8 +633,8 @@ 

                  Static methods

                  return next() + @staticmethod def _run( - self, listener: Listener, req: BoltRequest, resp: BoltResponse, @@ -644,7 +643,7 @@

                  Static methods

                  if next_was_not_called: return None - return self.listener_runner.run( + return req.context.listener_runner.run( request=req, response=resp, listener_name=get_name_for_callable(listener.ack_function), diff --git a/docs/static/api-docs/slack_bolt/workflows/step/step.html b/docs/static/api-docs/slack_bolt/workflows/step/step.html index 415fb4612..4c74b76f8 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/step.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/step.html @@ -612,7 +612,7 @@

                  Class variables

                  Static methods

                  -def to_listener_matchers(app_name: str, matchers: Optional[List[Union[Callable[..., bool], ListenerMatcher]]], base_logger: Optional[logging.Logger] = None) ‑> List[ListenerMatcher] +def to_listener_matchers(app_name: str, matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]], base_logger: Optional[logging.Logger] = None) ‑> List[ListenerMatcher]
                  diff --git a/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html b/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html index b8f52cb45..60b71fb99 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html @@ -37,7 +37,7 @@

                  Classes

                  class WorkflowStepMiddleware -(step: WorkflowStep, listener_runner: ThreadListenerRunner) +(step: WorkflowStep)

                  Base middleware for step from app specific ones

                  @@ -48,9 +48,8 @@

                  Classes

                  class WorkflowStepMiddleware(Middleware):
                       """Base middleware for step from app specific ones"""
                   
                  -    def __init__(self, step: WorkflowStep, listener_runner: ThreadListenerRunner):
                  +    def __init__(self, step: WorkflowStep):
                           self.step = step
                  -        self.listener_runner = listener_runner
                   
                       def process(
                           self,
                  @@ -78,8 +77,8 @@ 

                  Classes

                  return next() + @staticmethod def _run( - self, listener: Listener, req: BoltRequest, resp: BoltResponse, @@ -88,7 +87,7 @@

                  Classes

                  if next_was_not_called: return None - return self.listener_runner.run( + return req.context.listener_runner.run( request=req, response=resp, listener_name=get_name_for_callable(listener.ack_function), diff --git a/slack_bolt/version.py b/slack_bolt/version.py index bf4428776..20e481a80 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.20.1" +__version__ = "1.21.0" From 3c993d5748726b530d7e2c8d6cd0a42295cd20fe Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 17 Oct 2024 11:30:33 +0900 Subject: [PATCH 029/282] Add Agents & Assistants document page (#1175) Co-authored-by: Alissa Renz Co-authored-by: Fil Maj --- docs/content/basic/assistant.md | 240 ++++++++++++++++++ .../current/basic/assistant.md | 237 +++++++++++++++++ docs/sidebars.js | 1 + 3 files changed, 478 insertions(+) create mode 100644 docs/content/basic/assistant.md create mode 100644 docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md diff --git a/docs/content/basic/assistant.md b/docs/content/basic/assistant.md new file mode 100644 index 000000000..670dfebf4 --- /dev/null +++ b/docs/content/basic/assistant.md @@ -0,0 +1,240 @@ +--- +title: Agents & Assistants +lang: en +slug: /concepts/assistant +--- + +This guide focuses on how to implement Agents & Assistants using Bolt. For general information about the feature, please refer to the [API documentation](https://api.slack.com/docs/apps/ai). + +To get started, enable the **Agents & Assistants** feature on [the app configuration page](https://api.slack.com/apps). Add [`assistant:write`](https://api.slack.com/scopes/assistant:write), [`chat:write`](https://api.slack.com/scopes/chat:write), and [`im:history`](https://api.slack.com/scopes/im:history) to the **bot** scopes on the **OAuth & Permissions** page. Make sure to subscribe to [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started), [`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed), and [`message.im`](https://api.slack.com/events/message.im) events on the **Event Subscriptions** page. + +Please note that this feature requires a paid plan. If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. + +To handle assistant thread interactions with humans, although you can implement your agents [using `app.event(...)` listeners](event-listening) for `assistant_thread_started`, `assistant_thread_context_changed`, and `message` events, Bolt offers a simpler approach. You just need to create an `Assistant` instance, attach the needed event handlers to it, and then add the assistant to your `App` instance. + +```python +assistant = Assistant() + +# This listener is invoked when a human user opened an assistant thread +@assistant.thread_started +def start_assistant_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts): + # Send the first reply to the human who started chat with your app's assistant bot + say(":wave: Hi, how can I help you today?") + + # Setting suggested prompts is optional + set_suggested_prompts( + prompts=[ + # If the suggested prompt is long, you can use {"title": "short one to display", "message": "full prompt"} instead + "What does SLACK stand for?", + "When Slack was released?", + ], + ) + +# This listener is invoked when the human user sends a reply in the assistant thread +@assistant.user_message +def respond_in_assistant_thread( + payload: dict, + logger: logging.Logger, + context: BoltContext, + set_status: SetStatus, + say: Say, +): + try: + # Tell the human user the assistant bot acknowledges the request and is working on it + set_status("is typing...") + + # Collect the conversation history with this user + replies_in_thread = client.conversations_replies( + channel=context.channel_id, + ts=context.thread_ts, + oldest=context.thread_ts, + limit=10, + ) + messages_in_thread: List[Dict[str, str]] = [] + for message in replies_in_thread["messages"]: + role = "user" if message.get("bot_id") is None else "assistant" + messages_in_thread.append({"role": role, "content": message["text"]}) + + # Pass the latest prompt and chat history to the LLM (call_llm is your own code) + returned_message = call_llm(messages_in_thread) + + # Post the result in the assistant thread + say(text=returned_message) + + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + # Don't forget sending a message telling the error + # Without this, the status 'is typing...' won't be cleared, therefore the end-user is unable to continue the chat + say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + +# Enable this assistant middleware in your Bolt app +app.use(assistant) +``` + +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. + +When a user opens an Assistant thread while in a channel, the channel information is stored as the thread's `AssistantThreadContext` data. You can access this information by using the `get_thread_context` utility. The reason Bolt provides this utility is that the most recent thread context information is not included in the subsequent user message event payload data. Therefore, an app must store the context data when it is changed so that the app can refer to the data in message event listeners. + +When the user switches channels, the `assistant_thread_context_changed` event will be sent to your app. If you use the built-in `Assistant` middleware without any custom configuration (like the above code snippet does), the updated context data is automatically saved as message metadata of the first reply from the assistant bot. + +As long as you use the built-in approach, you don't need to store the context data within a datastore. The downside of this default behavior is the overhead of additional calls to the Slack API. These calls include those to `conversations.history` which are used to look up the stored message metadata that contains the thread context (via `get_thread_context`). + +To store context elsewhere, pass a custom `AssistantThreadContextStore` implementation to the `Assistant` constructor. We provide `FileAssistantThreadContextStore`, which is a reference implementation that uses the local file system: + +```python +# You can use your own thread_context_store if you want +from slack_bolt import FileAssistantThreadContextStore +assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) +``` + +Since this reference implementation relies on local files, it's not advised for use in production. For production apps, we recommend creating a class that inherits `AssistantThreadContextStore`. + +
                  + + +Block Kit interactions in the assistant thread + + +For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](https://api.slack.com/metadata) to trigger subsequent interactions with the user. + +For example, an app can display a button like "Summarize the referring channel" in the initial reply. When the user clicks the button and submits detailed information (such as the number of messages, days to check, the purpose of the summary, etc.), the app can handle that information and post a message that describes the request with structured metadata. + +By default, apps can't respond to their own bot messages (Bolt prevents infinite loops by default). However, if you pass `ignoring_self_assistant_message_events_enabled=False` to the `App` constructor and add a `bot_message` listener to your `Assistant` middleware, your app can continue processing the request as shown below: + +```python +app = App( + token=os.environ["SLACK_BOT_TOKEN"], + # This must be set to handle bot message events + ignoring_self_assistant_message_events_enabled=False, +) + +assistant = Assistant() + +# Refer to https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html to learn available listener arguments + +@assistant.thread_started +def start_assistant_thread(say: Say): + say( + text=":wave: Hi, how can I help you today?", + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": ":wave: Hi, how can I help you today?"}, + }, + { + "type": "actions", + "elements": [ + # You can have multiple buttons here + { + "type": "button", + "action_id": "assistant-generate-random-numbers", + "text": {"type": "plain_text", "text": "Generate random numbers"}, + "value": "clicked", + }, + ], + }, + ], + ) + +# This listener is invoked when the above button is clicked +@app.action("assistant-generate-random-numbers") +def configure_random_number_generation(ack: Ack, client: WebClient, body: dict): + ack() + client.views_open( + trigger_id=body["trigger_id"], + view={ + "type": "modal", + "callback_id": "configure_assistant_summarize_channel", + "title": {"type": "plain_text", "text": "My Assistant"}, + "submit": {"type": "plain_text", "text": "Submit"}, + "close": {"type": "plain_text", "text": "Cancel"}, + # Relay the assistant thread information to app.view listener + "private_metadata": json.dumps( + { + "channel_id": body["channel"]["id"], + "thread_ts": body["message"]["thread_ts"], + } + ), + "blocks": [ + { + "type": "input", + "block_id": "num", + "label": {"type": "plain_text", "text": "# of outputs"}, + # You can have this kind of predefined input from a user instead of parsing human text + "element": { + "type": "static_select", + "action_id": "input", + "placeholder": {"type": "plain_text", "text": "How many numbers do you need?"}, + "options": [ + {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + {"text": {"type": "plain_text", "text": "10"}, "value": "10"}, + {"text": {"type": "plain_text", "text": "20"}, "value": "20"}, + ], + "initial_option": {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + }, + } + ], + }, + ) + +# This listener is invoked when the above modal is submitted +@app.view("configure_assistant_summarize_channel") +def receive_random_number_generation_details(ack: Ack, client: WebClient, payload: dict): + ack() + num = payload["state"]["values"]["num"]["input"]["selected_option"]["value"] + thread = json.loads(payload["private_metadata"]) + + # Post a bot message with structured input data + # The following assistant.bot_message will continue processing + # If you prefer processing this request within this listener, it also works! + # If you don't need bot_message listener, no need to set ignoring_self_assistant_message_events_enabled=False + client.chat_postMessage( + channel=thread["channel_id"], + thread_ts=thread["thread_ts"], + text=f"OK, you need {num} numbers. I will generate it shortly!", + metadata={ + "event_type": "assistant-generate-random-numbers", + "event_payload": {"num": int(num)}, + }, + ) + +# This listener is invoked whenever your app's bot user posts a message +@assistant.bot_message +def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: Say, payload: dict): + try: + if payload.get("metadata", {}).get("event_type") == "assistant-generate-random-numbers": + # Handle the above random-number-generation request + set_status("is generating an array of random numbers...") + time.sleep(1) + nums: Set[str] = set() + num = payload["metadata"]["event_payload"]["num"] + while len(nums) < num: + nums.add(str(random.randint(1, 100))) + say(f"Here you are: {', '.join(nums)}") + else: + # nothing to do for this bot message + # If you want to add more patterns here, be careful not to cause infinite loop messaging + pass + + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + +# This listener is invoked when the human user posts a reply +@assistant.user_message +def respond_to_user_messages(logger: logging.Logger, set_status: SetStatus, say: Say): + try: + set_status("is typing...") + say("Please use the buttons in the first reply instead :bow:") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +# Enable this assistant middleware in your Bolt app +app.use(assistant) +``` + +
                  + + +Lastly, if you want to check full working example app, you can check [our sample repository](https://github.com/slack-samples/bolt-python-assistant-template) on GitHub. \ No newline at end of file diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md new file mode 100644 index 000000000..c9d991f13 --- /dev/null +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md @@ -0,0 +1,237 @@ +--- +title: エージェント・アシスタント +lang: en +slug: /concepts/assistant +--- + +このページは、Bolt を使ってエージェント・アシスタントを実装するための方法を紹介します。この機能に関する一般的な情報については、[こちらのドキュメントページ(英語)](https://api.slack.com/docs/apps/ai)を参照してください。 + +この機能を実装するためには、まず[アプリの設定画面](https://api.slack.com/apps)で **Agents & Assistants** 機能を有効にし、**OAuth & Permissions** のページで [`assistant:write`](https://api.slack.com/scopes/assistant:write)、[chat:write](https://api.slack.com/scopes/chat:write)、[`im:history`](https://api.slack.com/scopes/im:history) を**ボットの**スコープに追加し、**Event Subscriptions** のページで [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started)、[`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed)、[`message.im`](https://api.slack.com/events/message.im) イベントを有効にしてください。 + +また、この機能は Slack の有料プランでのみ利用可能です。もし開発用の有料プランのワークスペースをお持ちでない場合は、[Developer Program](https://api.slack.com/developer-program) に参加し、全ての有料プラン向け機能を利用可能なサンドボックス環境をつくることができます。 + +ユーザーとのアシスタントスレッド内でのやりとりを処理するには、`assistant_thread_started`、`assistant_thread_context_changed`、`message` イベントの `app.event(...)` リスナーを使うことも可能ですが、Bolt はよりシンプルなアプローチを提供しています。`Assistant` インスタンスを作り、それに必要なイベントリスナーを追加し、最後にこのアシスタント設定を `App` インスタンスに渡すだけでよいのです。 + +```python +assistant = Assistant() + +# ユーザーがアシスタントスレッドを開いたときに呼び出されます +@assistant.thread_started +def start_assistant_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts): + # ユーザーに対して最初の返信を送信します + say(":wave: Hi, how can I help you today?") + + # プロンプト例を送るのは必須ではありません + set_suggested_prompts( + prompts=[ + # もしプロンプトが長い場合は {"title": "表示する短いラベル", "message": "完全なプロンプト"} を使うことができます + "What does SLACK stand for?", + "When Slack was released?", + ], + ) + +# ユーザーがスレッド内で返信したときに呼び出されます +@assistant.user_message +def respond_in_assistant_thread( + payload: dict, + logger: logging.Logger, + context: BoltContext, + set_status: SetStatus, + say: Say, +): + try: + # ユーザーにこのbotがリクエストを受信して作業中であることを伝えます + set_status("is typing...") + + # 会話の履歴を取得します + replies_in_thread = client.conversations_replies( + channel=context.channel_id, + ts=context.thread_ts, + oldest=context.thread_ts, + limit=10, + ) + messages_in_thread: List[Dict[str, str]] = [] + for message in replies_in_thread["messages"]: + role = "user" if message.get("bot_id") is None else "assistant" + messages_in_thread.append({"role": role, "content": message["text"]}) + + # プロンプトと会話の履歴を LLM に渡します(この call_llm はあなた自身のコードです) + returned_message = call_llm(messages_in_thread) + + # 結果をアシスタントスレッドに送信します + say(text=returned_message) + + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + # エラーになった場合は必ずメッセージを送信するようにしてください + # そうしなかった場合、'is typing...' の表示のままになってしまい、ユーザーは会話を続けることができなくなります + say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + +# このミドルウェアを Bolt アプリに追加します +app.use(assistant) +``` + +リスナーに指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 + +ユーザーがチャンネルの横でアシスタンスレッドを開いた場合、そのチャンネルの情報はそのスレッドの `AssistantThreadContext` データとして保持され、 `get_thread_context` ユーティリティを使ってアクセスすることができます。Bolt がこのユーティリティを提供している理由は、後続のユーザーメッセージ投稿のイベントペイロードに最新のスレッドのコンテキスト情報は含まれないためです。そのため、アプリはコンテキスト情報が変更されたタイミングでそれを何らかの方法で保存し、後続のメッセージイベントのリスナーコードから参照できるようにする必要があります。 + +そのユーザーがチャンネルを切り替えた場合、`assistant_thread_context_changed` イベントがあなたのアプリに送信されます。(上記のコード例のように)組み込みの `Assistant` ミドルウェアをカスタム設定なしで利用している場合、この更新されたチャンネル情報は、自動的にこのアシスタントボットからの最初の返信のメッセージメタデータとして保存されます。これは、組み込みの仕組みを使う場合は、このコンテキスト情報を自前で用意したデータストアに保存する必要はないということです。この組み込みの仕組みの唯一の短所は、追加の Slack API 呼び出しによる処理時間のオーバーヘッドです。具体的には `get_thread_context` を実行したときに、この保存されたメッセージメタデータにアクセスするために `conversations.history` API が呼び出されます。 + +このデータを別の場所に保存したい場合、自前の `AssistantThreadContextStore` 実装を `Assistant` のコンストラクターに渡すことができます。リファレンス実装として、`FileAssistantThreadContextStore` というローカルファイルシステムを使って実装を提供しています: + +```python +# これはあくまで例であり、自前のものを渡すことができます +from slack_bolt import FileAssistantThreadContextStore +assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) +``` + +このリファレンス実装はローカルファイルに依存しており、本番環境での利用は推奨しません。本番アプリでは `AssistantThreadContextStore` を継承した自前のクラスを使うようにしてください。 + +
                  + + +アシスタントスレッドでの Block Kit インタラクション + + +より高度なユースケースでは、上のようなプロンプト例の提案ではなく Block Kit のボタンなどを使いたいという場合があるかもしれません。そして、後続の処理のために[構造化されたメッセージメタデータ](https://api.slack.com/metadata)を含むメッセージを送信したいという場合もあるでしょう。 + +例えば、アプリが最初の返信で「参照しているチャンネルを要約」のようなボタンを表示し、ユーザーがそれをクリックして、より詳細な情報(例:要約するメッセージ数・日数、要約の目的など)を送信、アプリがそれを構造化されたメータデータに整理した上でリクエスト内容をボットのメッセージとして送信するようなシナリオです。 + +デフォルトでは、アプリはそのアプリ自身から送信したボットメッセージに応答することはできません(Bolt にはあらかじめ無限ループを防止する制御が入っているため)。`ignoring_self_assistant_message_events_enabled=False` を `App` のコンストラクターに渡し、`bot_message` リスナーを `Assistant` ミドルウェアに追加すると、上記の例のようなリクエストを伝えるボットメッセージを使って処理を継続することができるようになります。 + +```python +app = App( + token=os.environ["SLACK_BOT_TOKEN"], + # bot message を受け取るには必ずこれを指定してください + ignoring_self_assistant_message_events_enabled=False, +) + +assistant = Assistant() + +# リスナーに指定可能な引数の一覧は https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html を参照してください + +@assistant.thread_started +def start_assistant_thread(say: Say): + say( + text=":wave: Hi, how can I help you today?", + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": ":wave: Hi, how can I help you today?"}, + }, + { + "type": "actions", + "elements": [ + # 複数のボタンを配置することが可能です + { + "type": "button", + "action_id": "assistant-generate-random-numbers", + "text": {"type": "plain_text", "text": "Generate random numbers"}, + "value": "clicked", + }, + ], + }, + ], + ) + +# 上のボタンがクリックされたときに実行されます +@app.action("assistant-generate-random-numbers") +def configure_random_number_generation(ack: Ack, client: WebClient, body: dict): + ack() + client.views_open( + trigger_id=body["trigger_id"], + view={ + "type": "modal", + "callback_id": "configure_assistant_summarize_channel", + "title": {"type": "plain_text", "text": "My Assistant"}, + "submit": {"type": "plain_text", "text": "Submit"}, + "close": {"type": "plain_text", "text": "Cancel"}, + # アシスタントスレッドの情報を app.view リスナーに引き継ぎます + "private_metadata": json.dumps( + { + "channel_id": body["channel"]["id"], + "thread_ts": body["message"]["thread_ts"], + } + ), + "blocks": [ + { + "type": "input", + "block_id": "num", + "label": {"type": "plain_text", "text": "# of outputs"}, + # 自然言語のテキストではなく、あらかじめ決められた形式の入力を受け取ることができます + "element": { + "type": "static_select", + "action_id": "input", + "placeholder": {"type": "plain_text", "text": "How many numbers do you need?"}, + "options": [ + {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + {"text": {"type": "plain_text", "text": "10"}, "value": "10"}, + {"text": {"type": "plain_text", "text": "20"}, "value": "20"}, + ], + "initial_option": {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, + }, + } + ], + }, + ) + +# 上のモーダルが送信されたときに実行されます +@app.view("configure_assistant_summarize_channel") +def receive_random_number_generation_details(ack: Ack, client: WebClient, payload: dict): + ack() + num = payload["state"]["values"]["num"]["input"]["selected_option"]["value"] + thread = json.loads(payload["private_metadata"]) + + # 構造化された入力情報とともにボットのメッセージを送信します + # 以下の assistant.bot_message リスナーが処理を継続します + # このリスナー内で処理したい場合はそれでも構いません! + # bot_message リスナーが必要ない場合は ignoring_self_assistant_message_events_enabled=False を設定する必要はありません + client.chat_postMessage( + channel=thread["channel_id"], + thread_ts=thread["thread_ts"], + text=f"OK, you need {num} numbers. I will generate it shortly!", + metadata={ + "event_type": "assistant-generate-random-numbers", + "event_payload": {"num": int(num)}, + }, + ) + +# このアプリのボットユーザーがメッセージを送信したときに実行されます +@assistant.bot_message +def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: Say, payload: dict): + try: + if payload.get("metadata", {}).get("event_type") == "assistant-generate-random-numbers": + # 上の random-number-generation リクエストを処理します + set_status("is generating an array of random numbers...") + time.sleep(1) + nums: Set[str] = set() + num = payload["metadata"]["event_payload"]["num"] + while len(nums) < num: + nums.add(str(random.randint(1, 100))) + say(f"Here you are: {', '.join(nums)}") + else: + # それ以外のパターンでは何もしません + # さらに他のパターンを追加する場合、メッセージ送信の無限ループを起こさないよう注意して実装してください + pass + + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + +# ユーザーが返信したときに実行されます +@assistant.user_message +def respond_to_user_messages(logger: logging.Logger, set_status: SetStatus, say: Say): + try: + set_status("is typing...") + say("Please use the buttons in the first reply instead :bow:") + except Exception as e: + logger.exception(f"Failed to respond to an inquiry: {e}") + say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + + +# このミドルウェアを Bolt アプリに追加します +app.use(assistant) +``` + +
                  + +最後に、動作する完全なサンプルコード例を確認したい場合は、私たちが GitHub 上で提供している[サンプルアプリのリポジトリ](https://github.com/slack-samples/bolt-python-assistant-template)をチェックしてみてください。 \ No newline at end of file diff --git a/docs/sidebars.js b/docs/sidebars.js index 03c7106e5..2fed41a3c 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -29,6 +29,7 @@ const sidebars = { type: 'category', label: 'Basic concepts', items: [ + 'basic/assistant', 'basic/message-listening', 'basic/message-sending', 'basic/event-listening', From 26b31f0306fd50c24a346d397ff69cbab370da88 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 17 Oct 2024 12:23:25 +0900 Subject: [PATCH 030/282] Fix documents --- docs/content/basic/assistant.md | 1 + .../docusaurus-plugin-content-docs/current/basic/assistant.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/content/basic/assistant.md b/docs/content/basic/assistant.md index 670dfebf4..6b39a9b62 100644 --- a/docs/content/basic/assistant.md +++ b/docs/content/basic/assistant.md @@ -37,6 +37,7 @@ def respond_in_assistant_thread( logger: logging.Logger, context: BoltContext, set_status: SetStatus, + client: WebClient, say: Say, ): try: diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md index c9d991f13..931fbc9f8 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md @@ -38,6 +38,7 @@ def respond_in_assistant_thread( context: BoltContext, set_status: SetStatus, say: Say, + client: WebClient, ): try: # ユーザーにこのbotがリクエストを受信して作業中であることを伝えます From 5aabb9db8b041f1014d2483b2f0223d2ccc5088d Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 17 Oct 2024 12:38:54 +0900 Subject: [PATCH 031/282] Tweak JP documents --- .../docusaurus-plugin-content-docs/current/basic/assistant.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md index 931fbc9f8..c09f7979e 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/basic/assistant.md @@ -74,7 +74,8 @@ app.use(assistant) リスナーに指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 -ユーザーがチャンネルの横でアシスタンスレッドを開いた場合、そのチャンネルの情報はそのスレッドの `AssistantThreadContext` データとして保持され、 `get_thread_context` ユーティリティを使ってアクセスすることができます。Bolt がこのユーティリティを提供している理由は、後続のユーザーメッセージ投稿のイベントペイロードに最新のスレッドのコンテキスト情報は含まれないためです。そのため、アプリはコンテキスト情報が変更されたタイミングでそれを何らかの方法で保存し、後続のメッセージイベントのリスナーコードから参照できるようにする必要があります。 + +ユーザーがチャンネルの横でアシスタントスレッドを開いた場合、そのチャンネルの情報は、そのスレッドの `AssistantThreadContext` データとして保持され、 `get_thread_context` ユーティリティを使ってアクセスすることができます。Bolt がこのユーティリティを提供している理由は、後続のユーザーメッセージ投稿のイベントペイロードに最新のスレッドのコンテキスト情報は含まれないためです。そのため、アプリはコンテキスト情報が変更されたタイミングでそれを何らかの方法で保存し、後続のメッセージイベントのリスナーコードから参照できるようにする必要があります。 そのユーザーがチャンネルを切り替えた場合、`assistant_thread_context_changed` イベントがあなたのアプリに送信されます。(上記のコード例のように)組み込みの `Assistant` ミドルウェアをカスタム設定なしで利用している場合、この更新されたチャンネル情報は、自動的にこのアシスタントボットからの最初の返信のメッセージメタデータとして保存されます。これは、組み込みの仕組みを使う場合は、このコンテキスト情報を自前で用意したデータストアに保存する必要はないということです。この組み込みの仕組みの唯一の短所は、追加の Slack API 呼び出しによる処理時間のオーバーヘッドです。具体的には `get_thread_context` を実行したときに、この保存されたメッセージメタデータにアクセスするために `conversations.history` API が呼び出されます。 From dd05b5d116dacfe167987f2fa2f0c8196f852d5b Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Tue, 22 Oct 2024 14:57:44 +0900 Subject: [PATCH 032/282] Fix a bug where parsing assistant thread message event fails for beta feature enabled apps (#1184) --- .../context/assistant/assistant_utilities.py | 3 ++- .../assistant/async_assistant_utilities.py | 3 ++- slack_bolt/context/assistant/internals.py | 9 +++++++ slack_bolt/request/internals.py | 8 +++++- tests/scenario_tests/test_events_assistant.py | 24 ++++++++++++++++++ .../test_events_assistant.py | 25 +++++++++++++++++++ 6 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 slack_bolt/context/assistant/internals.py diff --git a/slack_bolt/context/assistant/assistant_utilities.py b/slack_bolt/context/assistant/assistant_utilities.py index 6746ec286..9bcb33842 100644 --- a/slack_bolt/context/assistant/assistant_utilities.py +++ b/slack_bolt/context/assistant/assistant_utilities.py @@ -7,6 +7,7 @@ from slack_bolt.context.context import BoltContext from slack_bolt.context.say import Say +from .internals import has_channel_id_and_thread_ts from ..get_thread_context.get_thread_context import GetThreadContext from ..save_thread_context import SaveThreadContext from ..set_status import SetStatus @@ -32,7 +33,7 @@ def __init__( self.client = context.client self.thread_context_store = thread_context_store or DefaultAssistantThreadContextStore(context) - if self.payload.get("assistant_thread") is not None: + if has_channel_id_and_thread_ts(self.payload): # assistant_thread_started thread = self.payload["assistant_thread"] self.channel_id = thread["channel_id"] diff --git a/slack_bolt/context/assistant/async_assistant_utilities.py b/slack_bolt/context/assistant/async_assistant_utilities.py index b0f8a1fae..5a7324e99 100644 --- a/slack_bolt/context/assistant/async_assistant_utilities.py +++ b/slack_bolt/context/assistant/async_assistant_utilities.py @@ -10,6 +10,7 @@ from slack_bolt.context.async_context import AsyncBoltContext from slack_bolt.context.say.async_say import AsyncSay +from .internals import has_channel_id_and_thread_ts from ..get_thread_context.async_get_thread_context import AsyncGetThreadContext from ..save_thread_context.async_save_thread_context import AsyncSaveThreadContext from ..set_status.async_set_status import AsyncSetStatus @@ -35,7 +36,7 @@ def __init__( self.client = context.client self.thread_context_store = thread_context_store or DefaultAsyncAssistantThreadContextStore(context) - if self.payload.get("assistant_thread") is not None: + if has_channel_id_and_thread_ts(self.payload): # assistant_thread_started thread = self.payload["assistant_thread"] self.channel_id = thread["channel_id"] diff --git a/slack_bolt/context/assistant/internals.py b/slack_bolt/context/assistant/internals.py new file mode 100644 index 000000000..ee449c31b --- /dev/null +++ b/slack_bolt/context/assistant/internals.py @@ -0,0 +1,9 @@ +def has_channel_id_and_thread_ts(payload: dict) -> bool: + """Verifies if the given payload has both channel_id and thread_ts under assistant_thread property. + This data pattern is available for assistant_* events. + """ + return ( + payload.get("assistant_thread") is not None + and payload["assistant_thread"].get("channel_id") is not None + and payload["assistant_thread"].get("thread_ts") is not None + ) diff --git a/slack_bolt/request/internals.py b/slack_bolt/request/internals.py index b04f336bf..014a8134a 100644 --- a/slack_bolt/request/internals.py +++ b/slack_bolt/request/internals.py @@ -220,8 +220,14 @@ def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]: # Thus, blindly setting this thread_ts to say utility can break existing apps' behaviors. if is_assistant_event(payload): event = payload["event"] - if event.get("assistant_thread") is not None: + if ( + event.get("assistant_thread") is not None + and event["assistant_thread"].get("channel_id") is not None + and event["assistant_thread"].get("thread_ts") is not None + ): # assistant_thread_started, assistant_thread_context_changed + # "assistant_thread" property can exist for message event without channel_id and thread_ts + # Thus, the above if check verifies these properties exist return event["assistant_thread"]["thread_ts"] elif event.get("channel") is not None: if event.get("thread_ts") is not None: diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index ed3026d12..f5ccbb565 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -82,6 +82,11 @@ def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): assert response.status == 200 assert_target_called() + request = BoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + request = BoltRequest(body=message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 @@ -163,6 +168,25 @@ def build_payload(event: dict) -> dict: ) +user_message_event_body_with_assistant_thread = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + "assistant_thread": {"XXX": "YYY"}, + } +) + message_changed_event_body = build_payload( { "type": "message", diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index f8ed97af9..bf71e914b 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -97,6 +97,11 @@ async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context assert response.status == 200 await assert_target_called() + request = AsyncBoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 @@ -178,6 +183,26 @@ def build_payload(event: dict) -> dict: ) +user_message_event_body_with_assistant_thread = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + "assistant_thread": {"XXX": "YYY"}, + } +) + + message_changed_event_body = build_payload( { "type": "message", From 18a91356a5b1bdd63a7536a70370c1dff1c31dce Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Tue, 22 Oct 2024 14:59:36 +0900 Subject: [PATCH 033/282] version 1.21.1 --- .../assistant/assistant_utilities.html | 2 +- .../assistant/async_assistant_utilities.html | 2 +- .../slack_bolt/context/assistant/index.html | 5 ++ .../context/assistant/internals.html | 70 +++++++++++++++++++ slack_bolt/version.py | 2 +- 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 docs/static/api-docs/slack_bolt/context/assistant/internals.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html index c2d0be5bf..cca9002da 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html @@ -63,7 +63,7 @@

                  Classes

                  self.client = context.client self.thread_context_store = thread_context_store or DefaultAssistantThreadContextStore(context) - if self.payload.get("assistant_thread") is not None: + if has_channel_id_and_thread_ts(self.payload): # assistant_thread_started thread = self.payload["assistant_thread"] self.channel_id = thread["channel_id"] diff --git a/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html b/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html index 4de1dbddf..8bbbfd414 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html @@ -63,7 +63,7 @@

                  Classes

                  self.client = context.client self.thread_context_store = thread_context_store or DefaultAsyncAssistantThreadContextStore(context) - if self.payload.get("assistant_thread") is not None: + if has_channel_id_and_thread_ts(self.payload): # assistant_thread_started thread = self.payload["assistant_thread"] self.channel_id = thread["channel_id"] diff --git a/docs/static/api-docs/slack_bolt/context/assistant/index.html b/docs/static/api-docs/slack_bolt/context/assistant/index.html index c19bafdea..6ff8a0a1a 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/index.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/index.html @@ -37,6 +37,10 @@

                  Sub-modules

                  +
                  slack_bolt.context.assistant.internals
                  +
                  +
                  +
                  slack_bolt.context.assistant.thread_context
                  @@ -68,6 +72,7 @@

                  Sub-modules

                  diff --git a/docs/static/api-docs/slack_bolt/context/assistant/internals.html b/docs/static/api-docs/slack_bolt/context/assistant/internals.html new file mode 100644 index 000000000..6e576c1f5 --- /dev/null +++ b/docs/static/api-docs/slack_bolt/context/assistant/internals.html @@ -0,0 +1,70 @@ + + + + + + +slack_bolt.context.assistant.internals API documentation + + + + + + + + + + + +
                  +
                  +
                  +

                  Module slack_bolt.context.assistant.internals

                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  +

                  Functions

                  +
                  +
                  +def has_channel_id_and_thread_ts(payload: dict) ‑> bool +
                  +
                  +

                  Verifies if the given payload has both channel_id and thread_ts under assistant_thread property. +This data pattern is available for assistant_* events.

                  +
                  +
                  +
                  +
                  +
                  +
                  + +
                  + + + diff --git a/slack_bolt/version.py b/slack_bolt/version.py index 20e481a80..09ec72149 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.21.0" +__version__ = "1.21.1" From 38167c4e0e8be6857043ff2e2085b4d216fa6fdb Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Fri, 25 Oct 2024 08:15:25 +0900 Subject: [PATCH 034/282] Improve metadata resolution timing in assistant app's say method (#1186) --- slack_bolt/context/assistant/assistant_utilities.py | 11 +++++++---- slack_bolt/context/say/say.py | 12 +++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/slack_bolt/context/assistant/assistant_utilities.py b/slack_bolt/context/assistant/assistant_utilities.py index 9bcb33842..53500efdb 100644 --- a/slack_bolt/context/assistant/assistant_utilities.py +++ b/slack_bolt/context/assistant/assistant_utilities.py @@ -63,14 +63,17 @@ def set_suggested_prompts(self) -> SetSuggestedPrompts: @property def say(self) -> Say: + def build_metadata() -> Optional[dict]: + thread_context = self.get_thread_context() + if thread_context is not None: + return {"event_type": "assistant_thread_context", "event_payload": thread_context} + return None + return Say( self.client, channel=self.channel_id, thread_ts=self.thread_ts, - metadata={ - "event_type": "assistant_thread_context", - "event_payload": self.get_thread_context(), - }, + build_metadata=build_metadata, ) @property diff --git a/slack_bolt/context/say/say.py b/slack_bolt/context/say/say.py index 6c0127a62..6cfbcd801 100644 --- a/slack_bolt/context/say/say.py +++ b/slack_bolt/context/say/say.py @@ -1,4 +1,4 @@ -from typing import Optional, Union, Dict, Sequence +from typing import Optional, Union, Dict, Sequence, Callable from slack_sdk import WebClient from slack_sdk.models.attachments import Attachment @@ -15,6 +15,7 @@ class Say: channel: Optional[str] thread_ts: Optional[str] metadata: Optional[Union[Dict, Metadata]] + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] def __init__( self, @@ -22,11 +23,13 @@ def __init__( channel: Optional[str], thread_ts: Optional[str] = None, metadata: Optional[Union[Dict, Metadata]] = None, + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] = None, ): self.client = client self.channel = channel self.thread_ts = thread_ts self.metadata = metadata + self.build_metadata = build_metadata def __call__( self, @@ -52,6 +55,8 @@ def __call__( text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response + if metadata is None: + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata return self.client.chat_postMessage( # type: ignore[union-attr] channel=channel or self.channel, # type: ignore[arg-type] text=text, @@ -68,7 +73,7 @@ def __call__( mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata or self.metadata, + metadata=metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): @@ -78,7 +83,8 @@ def __call__( if "thread_ts" not in message: message["thread_ts"] = thread_ts or self.thread_ts if "metadata" not in message: - message["metadata"] = metadata or self.metadata + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata + message["metadata"] = metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") From 9de07b1a70f09a60ba6c0c5846eddf9734cbc7b6 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Fri, 25 Oct 2024 14:41:38 +0900 Subject: [PATCH 035/282] version 1.21.2 --- .../assistant/assistant_utilities.html | 22 ++++++++++++------- .../slack_bolt/context/say/index.html | 17 +++++++++++--- .../api-docs/slack_bolt/context/say/say.html | 17 +++++++++++--- docs/static/api-docs/slack_bolt/index.html | 17 +++++++++++--- slack_bolt/version.py | 2 +- 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html index cca9002da..fcdc21ca4 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html @@ -93,14 +93,17 @@

                  Classes

                  @property def say(self) -> Say: + def build_metadata() -> Optional[dict]: + thread_context = self.get_thread_context() + if thread_context is not None: + return {"event_type": "assistant_thread_context", "event_payload": thread_context} + return None + return Say( self.client, channel=self.channel_id, thread_ts=self.thread_ts, - metadata={ - "event_type": "assistant_thread_context", - "event_payload": self.get_thread_context(), - }, + build_metadata=build_metadata, ) @property @@ -169,14 +172,17 @@

                  Instance variables

                  @property
                   def say(self) -> Say:
                  +    def build_metadata() -> Optional[dict]:
                  +        thread_context = self.get_thread_context()
                  +        if thread_context is not None:
                  +            return {"event_type": "assistant_thread_context", "event_payload": thread_context}
                  +        return None
                  +
                       return Say(
                           self.client,
                           channel=self.channel_id,
                           thread_ts=self.thread_ts,
                  -        metadata={
                  -            "event_type": "assistant_thread_context",
                  -            "event_payload": self.get_thread_context(),
                  -        },
                  +        build_metadata=build_metadata,
                       )
                  diff --git a/docs/static/api-docs/slack_bolt/context/say/index.html b/docs/static/api-docs/slack_bolt/context/say/index.html index 7799c5a21..c4bfed42f 100644 --- a/docs/static/api-docs/slack_bolt/context/say/index.html +++ b/docs/static/api-docs/slack_bolt/context/say/index.html @@ -52,7 +52,7 @@

                  Classes

                  class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None) +(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None, build_metadata: Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]] = None)
                  @@ -65,6 +65,7 @@

                  Classes

                  channel: Optional[str] thread_ts: Optional[str] metadata: Optional[Union[Dict, Metadata]] + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] def __init__( self, @@ -72,11 +73,13 @@

                  Classes

                  channel: Optional[str], thread_ts: Optional[str] = None, metadata: Optional[Union[Dict, Metadata]] = None, + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] = None, ): self.client = client self.channel = channel self.thread_ts = thread_ts self.metadata = metadata + self.build_metadata = build_metadata def __call__( self, @@ -102,6 +105,8 @@

                  Classes

                  text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response + if metadata is None: + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata return self.client.chat_postMessage( # type: ignore[union-attr] channel=channel or self.channel, # type: ignore[arg-type] text=text, @@ -118,7 +123,7 @@

                  Classes

                  mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata or self.metadata, + metadata=metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): @@ -128,7 +133,8 @@

                  Classes

                  if "thread_ts" not in message: message["thread_ts"] = thread_ts or self.thread_ts if "metadata" not in message: - message["metadata"] = metadata or self.metadata + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata + message["metadata"] = metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -137,6 +143,10 @@

                  Classes

                  Class variables

                  +
                  var build_metadata : Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]]
                  +
                  +
                  +
                  var channel : Optional[str]
                  @@ -180,6 +190,7 @@

                  Class variables

                • Say

                    +
                  • build_metadata
                  • channel
                  • client
                  • metadata
                  • diff --git a/docs/static/api-docs/slack_bolt/context/say/say.html b/docs/static/api-docs/slack_bolt/context/say/say.html index e25077d20..db3bc1675 100644 --- a/docs/static/api-docs/slack_bolt/context/say/say.html +++ b/docs/static/api-docs/slack_bolt/context/say/say.html @@ -37,7 +37,7 @@

                    Classes

                    class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None) +(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None, build_metadata: Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]] = None)
                    @@ -50,6 +50,7 @@

                    Classes

                    channel: Optional[str] thread_ts: Optional[str] metadata: Optional[Union[Dict, Metadata]] + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] def __init__( self, @@ -57,11 +58,13 @@

                    Classes

                    channel: Optional[str], thread_ts: Optional[str] = None, metadata: Optional[Union[Dict, Metadata]] = None, + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] = None, ): self.client = client self.channel = channel self.thread_ts = thread_ts self.metadata = metadata + self.build_metadata = build_metadata def __call__( self, @@ -87,6 +90,8 @@

                    Classes

                    text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response + if metadata is None: + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata return self.client.chat_postMessage( # type: ignore[union-attr] channel=channel or self.channel, # type: ignore[arg-type] text=text, @@ -103,7 +108,7 @@

                    Classes

                    mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata or self.metadata, + metadata=metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): @@ -113,7 +118,8 @@

                    Classes

                    if "thread_ts" not in message: message["thread_ts"] = thread_ts or self.thread_ts if "metadata" not in message: - message["metadata"] = metadata or self.metadata + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata + message["metadata"] = metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -122,6 +128,10 @@

                    Classes

                    Class variables

                    +
                    var build_metadata : Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]]
                    +
                    +
                    +
                    var channel : Optional[str]
                    @@ -158,6 +168,7 @@

                    Class variables

                  • Say

                      +
                    • build_metadata
                    • channel
                    • client
                    • metadata
                    • diff --git a/docs/static/api-docs/slack_bolt/index.html b/docs/static/api-docs/slack_bolt/index.html index 17fc98dfe..8ba57b21d 100644 --- a/docs/static/api-docs/slack_bolt/index.html +++ b/docs/static/api-docs/slack_bolt/index.html @@ -4300,7 +4300,7 @@

                      Class variables

                  • class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None) +(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None, build_metadata: Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]] = None)
                    @@ -4313,6 +4313,7 @@

                    Class variables

                    channel: Optional[str] thread_ts: Optional[str] metadata: Optional[Union[Dict, Metadata]] + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] def __init__( self, @@ -4320,11 +4321,13 @@

                    Class variables

                    channel: Optional[str], thread_ts: Optional[str] = None, metadata: Optional[Union[Dict, Metadata]] = None, + build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] = None, ): self.client = client self.channel = channel self.thread_ts = thread_ts self.metadata = metadata + self.build_metadata = build_metadata def __call__( self, @@ -4350,6 +4353,8 @@

                    Class variables

                    text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response + if metadata is None: + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata return self.client.chat_postMessage( # type: ignore[union-attr] channel=channel or self.channel, # type: ignore[arg-type] text=text, @@ -4366,7 +4371,7 @@

                    Class variables

                    mrkdwn=mrkdwn, link_names=link_names, parse=parse, - metadata=metadata or self.metadata, + metadata=metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): @@ -4376,7 +4381,8 @@

                    Class variables

                    if "thread_ts" not in message: message["thread_ts"] = thread_ts or self.thread_ts if "metadata" not in message: - message["metadata"] = metadata or self.metadata + metadata = self.build_metadata() if self.build_metadata is not None else self.metadata + message["metadata"] = metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") @@ -4385,6 +4391,10 @@

                    Class variables

                    Class variables

                    +
                    var build_metadata : Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]]
                    +
                    +
                    +
                    var channel : Optional[str]
                    @@ -4799,6 +4809,7 @@

                    Say

                      +
                    • build_metadata
                    • channel
                    • client
                    • metadata
                    • diff --git a/slack_bolt/version.py b/slack_bolt/version.py index 09ec72149..7b7d241dc 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.21.1" +__version__ = "1.21.2" From 888a11fa1da7cab04772bf159f0e91ed1aa73c18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:05:32 -0400 Subject: [PATCH 036/282] chore(deps): bump http-proxy-middleware from 2.0.6 to 2.0.7 in /docs (#1188) Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index c6f46a33f..62097d531 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -7435,9 +7435,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", From 131f8e1fc9fa1420ca1ee9df6f13b0e9feaa7e9e Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Tue, 29 Oct 2024 08:15:17 +0900 Subject: [PATCH 037/282] Add title argument to SetSuggestedPrompts arguments (#1187) --- .../set_suggested_prompts/async_set_suggested_prompts.py | 9 +++++++-- .../set_suggested_prompts/set_suggested_prompts.py | 9 +++++++-- tests/scenario_tests/test_events_assistant.py | 3 +++ tests/scenario_tests_async/test_events_assistant.py | 4 ++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py index 76f827732..aeeb244d7 100644 --- a/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py +++ b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py @@ -1,4 +1,4 @@ -from typing import List, Dict, Union +from typing import List, Dict, Union, Optional from slack_sdk.web.async_client import AsyncWebClient from slack_sdk.web.async_slack_response import AsyncSlackResponse @@ -19,7 +19,11 @@ def __init__( self.channel_id = channel_id self.thread_ts = thread_ts - async def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> AsyncSlackResponse: + async def __call__( + self, + prompts: List[Union[str, Dict[str, str]]], + title: Optional[str] = None, + ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): @@ -31,4 +35,5 @@ async def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> AsyncSlac channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, + title=title, ) diff --git a/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py index 3714f4830..fc9304b17 100644 --- a/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py +++ b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py @@ -1,4 +1,4 @@ -from typing import List, Dict, Union +from typing import List, Dict, Union, Optional from slack_sdk import WebClient from slack_sdk.web import SlackResponse @@ -19,7 +19,11 @@ def __init__( self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse: + def __call__( + self, + prompts: List[Union[str, Dict[str, str]]], + title: Optional[str] = None, + ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): @@ -31,4 +35,5 @@ def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse: channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, + title=title, ) diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index f5ccbb565..07f7ede53 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -46,6 +46,9 @@ def start_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts, context: assert context.thread_ts == "1726133698.626339" say("Hi, how can I help you today?") set_suggested_prompts(prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}]) + set_suggested_prompts( + prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}], title="foo" + ) state["called"] = True @assistant.thread_context_changed diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index bf71e914b..ac2c734c5 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -61,6 +61,10 @@ async def start_thread(say: AsyncSay, set_suggested_prompts: AsyncSetSuggestedPr await set_suggested_prompts( prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}] ) + await set_suggested_prompts( + prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}], + title="foo", + ) state["called"] = True @assistant.thread_context_changed From 8e148ed0445e480deef90440d795af6519578719 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:28:39 -0700 Subject: [PATCH 038/282] Docs: salesforce compliance adjustment (#1190) --- docs/README.md | 4 +- docs/docusaurus.config.js | 112 ++------------------------------------ docs/footerConfig.js | 19 +++++++ docs/navbarConfig.js | 88 ++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 108 deletions(-) create mode 100644 docs/footerConfig.js create mode 100644 docs/navbarConfig.js diff --git a/docs/README.md b/docs/README.md index 22a279f04..c1f79adfb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,7 +30,9 @@ website/ ├── src/ │ ├── pages/ (stuff that isn't docs. This is empty for this repo!) │ └── theme/ (only contains the 404 page) -├── docusaurus.config.js (main config file. also where to set navbar/footer) +├── docusaurus.config.js (main config file) +├── footerConfig.js (footer. go to main repo to change) +├── navbarConfig.js (navbar. go to main repo to change) └── sidebar.js (manually set where the docs are in the sidebar.) ``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 51b9e06c7..d6c3aa5a7 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -1,17 +1,12 @@ -// @ts-check -// `@type` JSDoc annotations allow editor autocompletion and type checking -// (when paired with `@ts-check`). -// There are various equivalent ways to declare your Docusaurus config. -// See: https://docusaurus.io/docs/api/docusaurus-config - -import { themes as prismThemes } from "prism-react-renderer"; +import { themes as prismThemes } from 'prism-react-renderer'; +const footer = require('./footerConfig'); +const navbar = require('./navbarConfig'); /** @type {import('@docusaurus/types').Config} */ const config = { title: "Bolt for Python", tagline: "Official frameworks, libraries, and SDKs for Slack developers", favicon: "img/favicon.ico", - url: "https://tools.slack.dev", baseUrl: "/bolt-python/", organizationName: "slackapi", @@ -42,9 +37,6 @@ const config = { theme: { customCss: "./src/css/custom.css", }, - gtag: { - trackingID: 'G-9H1YZW28BG', - }, }), ], ], @@ -79,102 +71,8 @@ const config = { autoCollapseCategories: true, }, }, - navbar: { - title: "Slack Developer Tools", - logo: { - alt: "Slack logo", - src: "img/slack-logo.svg", - href: "https://tools.slack.dev", - target: "_self", - }, - items: [ - { - type: "dropdown", - label: "Bolt", - position: "left", - items: [ - { - label: "Java", - to: "https://tools.slack.dev/java-slack-sdk/guides/bolt-basics", - target: "_self", - }, - { - label: "JavaScript", - to: "https://tools.slack.dev/bolt-js", - target: "_self", - }, - { - label: "Python", - to: "https://tools.slack.dev/bolt-python", - target: "_self", - }, - ], - }, - { - type: "dropdown", - label: "SDKs", - position: "left", - items: [ - { - label: "Java Slack SDK", - to: "https://tools.slack.dev/java-slack-sdk/", - target: "_self", - }, - { - label: "Node Slack SDK", - to: "https://tools.slack.dev/node-slack-sdk/", - target: "_self", - }, - { - label: "Python Slack SDK", - to: "https://tools.slack.dev/python-slack-sdk/", - target: "_self", - }, - { - label: "Deno Slack SDK", - to: "https://api.slack.com/automation/quickstart", - target: "_self", - }, - ], - }, - { - type: "dropdown", - label: "Community", - position: "left", - items: [ - { - label: "Community tools", - to: "https://tools.slack.dev/community-tools", - target: "_self", - }, - { - label: "Slack Community", - to: "https://slackcommunity.com/", - target: "_self", - }, - ], - }, - { - to: "https://api.slack.com/docs", - label: "API Docs", - target: "_self", - }, - { - type: "localeDropdown", - position: "right", - }, - { - "aria-label": "GitHub Repository", - className: "navbar-github-link", - href: "https://github.com/slackapi/bolt-python", - position: "right", - target: "_self", - }, - ], - }, - footer: { - copyright: `

                      Made with ♡ by Slack and pals like you

                      `, - }, + navbar, + footer, prism: { // switch to alucard when available in prism? theme: prismThemes.github, diff --git a/docs/footerConfig.js b/docs/footerConfig.js new file mode 100644 index 000000000..e3e10c571 --- /dev/null +++ b/docs/footerConfig.js @@ -0,0 +1,19 @@ +const footer = { + links: [ + { + items: [ + { + html: ` +

                      + ©2024 Slack Technologies, LLC, a Salesforce company. All rights reserved. Various trademarks held by their respective owners. + `, + }, + ], + }, + ], +}; + +module.exports = footer; diff --git a/docs/navbarConfig.js b/docs/navbarConfig.js new file mode 100644 index 000000000..f4277367a --- /dev/null +++ b/docs/navbarConfig.js @@ -0,0 +1,88 @@ +const navbar = { + title: 'Slack Developer Tools', + logo: { + src: 'img/slack-logo.svg', + }, + items: [ + { + type: 'dropdown', + label: 'Bolt', + position: 'left', + items: [ + { + label: 'Java', + to: 'https://tools.slack.dev/java-slack-sdk/guides/bolt-basics', + target: '_self', + }, + { + label: 'JavaScript', + to: 'https://tools.slack.dev/bolt-js', + target: '_self', + }, + { + label: 'Python', + to: 'https://tools.slack.dev/bolt-python', + target: '_self', + }, + ], + }, + { + type: 'dropdown', + label: 'SDKs', + position: 'left', + items: [ + { + label: 'Java Slack SDK', + to: 'https://tools.slack.dev/java-slack-sdk/', + target: '_self', + }, + { + label: 'Node Slack SDK', + to: 'https://tools.slack.dev/node-slack-sdk/', + target: '_self', + }, + { + label: 'Python Slack SDK', + to: 'https://tools.slack.dev/python-slack-sdk/', + target: '_self', + }, + { + label: 'Deno Slack SDK', + to: 'https://api.slack.com/automation/quickstart', + target: '_self', + }, + ], + }, + { + type: 'dropdown', + label: 'Community', + position: 'left', + items: [ + { + label: 'Community tools', + to: 'https://tools.slack.dev/community-tools', + target: '_self', + }, + { + label: 'Slack Community', + to: 'https://slackcommunity.com/', + target: '_self', + }, + ], + }, + { + to: 'https://api.slack.com/docs', + label: 'API Docs', + target: '_self', + }, + { + 'aria-label': 'GitHub Repository', + className: 'navbar-github-link', + href: 'https://github.com/slackapi', + position: 'right', + target: '_self', + }, + ], +}; + +module.exports = navbar; From de734ef48fc6d99ba3f07c124abf905beef15bae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:21:38 -0700 Subject: [PATCH 039/282] chore(deps): bump @mdx-js/react from 3.0.1 to 3.1.0 in /docs (#1193) Bumps [@mdx-js/react](https://github.com/mdx-js/mdx/tree/HEAD/packages/react) from 3.0.1 to 3.1.0. - [Release notes](https://github.com/mdx-js/mdx/releases) - [Changelog](https://github.com/mdx-js/mdx/blob/main/changelog.md) - [Commits](https://github.com/mdx-js/mdx/commits/3.1.0/packages/react) --- updated-dependencies: - dependency-name: "@mdx-js/react" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 8 ++++---- docs/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 62097d531..19d882897 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -11,7 +11,7 @@ "@docusaurus/core": "3.5.2", "@docusaurus/plugin-client-redirects": "^3.5.2", "@docusaurus/preset-classic": "3.5.2", - "@mdx-js/react": "^3.0.0", + "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", "prism-react-renderer": "^2.4.0", @@ -2972,9 +2972,9 @@ } }, "node_modules/@mdx-js/react": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", - "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", "dependencies": { "@types/mdx": "^2.0.0" }, diff --git a/docs/package.json b/docs/package.json index c295a8abf..f9853246c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -17,7 +17,7 @@ "@docusaurus/core": "3.5.2", "@docusaurus/plugin-client-redirects": "^3.5.2", "@docusaurus/preset-classic": "3.5.2", - "@mdx-js/react": "^3.0.0", + "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", "prism-react-renderer": "^2.4.0", From 821a979a9871596c561ec39d1d9f71e809e4dd54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:51:34 -0700 Subject: [PATCH 040/282] chore(deps): bump mypy from 1.11.2 to 1.13.0 (#1195) Bumps [mypy](https://github.com/python/mypy) from 1.11.2 to 1.13.0. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.11.2...v1.13.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 38c4d6930..3cb265f63 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.11.2 +mypy==1.13.0 flake8==6.0.0 black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version From 12b449825507f4b3304c87e56e9c1f927a420265 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:57:41 -0700 Subject: [PATCH 041/282] chore(deps): update pytest-cov requirement from <6,>=3 to >=3,<7 (#1196) Updates the requirements on [pytest-cov](https://github.com/pytest-dev/pytest-cov) to permit the latest version. - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v3.0.0...v6.0.0) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Brooks --- requirements/testing_without_asyncio.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/testing_without_asyncio.txt b/requirements/testing_without_asyncio.txt index 356ece87e..754889faf 100644 --- a/requirements/testing_without_asyncio.txt +++ b/requirements/testing_without_asyncio.txt @@ -1,3 +1,3 @@ # pip install -r requirements/testing_without_asyncio.txt pytest>=6.2.5,<8.4 # https://github.com/tornadoweb/tornado/issues/3375 -pytest-cov>=3,<6 +pytest-cov>=3,<7 From f6fc477aba577a4643c7e89484f55506b8883d7e Mon Sep 17 00:00:00 2001 From: "slackapi[bot]" <186980925+slackapi[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:19:29 -0700 Subject: [PATCH 042/282] Update config files (#1192) Co-authored-by: slackapi[bot] <186980925+slackapi[bot]@users.noreply.github.com> Co-authored-by: @zimeg --- docs/navbarConfig.js | 1 + docs/src/css/custom.css | 138 +++++++++++++++++++++++++--------------- 2 files changed, 87 insertions(+), 52 deletions(-) diff --git a/docs/navbarConfig.js b/docs/navbarConfig.js index f4277367a..e9deaab7d 100644 --- a/docs/navbarConfig.js +++ b/docs/navbarConfig.js @@ -2,6 +2,7 @@ const navbar = { title: 'Slack Developer Tools', logo: { src: 'img/slack-logo.svg', + href: 'https://tools.slack.dev', }, items: [ { diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index be9fbdc1f..85adb3538 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -1,31 +1,16 @@ -/** - * Any CSS included here will be global. The classic template - * bundles Infima by default. Infima is a CSS framework designed to - * work well for content-centric websites. - */ - :root { /* set hex colors here pls */ --aubergine: #4a154b; - /* aubergine-active is used in light mode. use something like #853c8c if you use as a link vs black text ( 3:1 contr) */ --aubergine-active: #7c3085; - - /* aubergine-light is used in dark mode. #b681b5 is another one. i just made both up */ - --aubergine-light: #ce70cc; - /* horchata is that beige color we use a lot */ --horchata: #f4ede4; - - /* slack-blue is 36C5F0. used for dark-mode links */ - --slack-link: #36c5f0; - --slack-blue: #36c5f0; + /* cloud blue from slack.dev. used for dark-mode links */ + --slack-cloud-blue: #1ab9ff; /* slack marketing color for links 1264A3. used for light-mode links */ --slack-dark-blue: #1264a3; - - --grey: #868686; - --white: #FFFFFF; + --dim: #eef2f6; } /* resets striped tables that hurt me eyes */ @@ -33,43 +18,55 @@ table tr:nth-child(even) { background-color: inherit; } -p a { - text-decoration: underline; - color: var(--slack-link); +/* changing the links to blue for accessibility */ +p a, .markdown a { + color: var(--slack-cloud-blue); } -.markdown a { - color: var(--slack-link); - text-decoration: underline; +a:hover { + color: var(--slack-cloud-blue); } /* adjusting for light and dark modes */ [data-theme="light"] { + --docusaurus-highlighted-code-line-bg: var(--dim); --ifm-color-primary: var(--aubergine-active); --ifm-footer-background-color: var(--horchata); - --slack-link: var(--slack-dark-blue); + --ifm-footer-color: black; + --slack-cloud-blue: var(--slack-dark-blue); + --ifm-table-stripe-background: var(--horchata); } [data-theme="dark"] { - --ifm-color-primary: var(--aubergine-light); - --ifm-navbar-background-color: var(--aubergine); - --ifm-footer-background-color: var(--aubergine); - --slack-link: var(--slack-blue); -} - -html[data-theme="dark"] { --docusaurus-highlighted-code-line-bg: rgb(0 0 0 / 30%); + --ifm-color-primary: var(--slack-cloud-blue); + --ifm-navbar-background-color: var(--aubergine) !important; + --ifm-footer-background-color: var(--aubergine) !important; + --ifm-footer-color: white; } -/* bolding Toc for contrast */ +/* bolding ToC for contrast */ .table-of-contents__link--active { font-weight: bold; } -/* only uncomment for home page -- colors white space on v tall screens */ -/* .main-wrapper { - background: var(--horchata); -} */ +/* removing ToC line */ +.table-of-contents__left-border { + border-left: none !important; +} + +/* increasing name of SDK in sidebar */ +.sidebar-title { + font-size: 1.25em; /* Adjust the size as needed */ + font-weight: bold; + color: #000; +} + +/* removing sidebar line and adding space to match ToC */ +.theme-doc-sidebar-container { + border-right: none !important; + margin-right: 2rem; +} /* announcement bar up top */ div[class^="announcementBar_"] { @@ -78,17 +75,7 @@ div[class^="announcementBar_"] { background: var(--horchata); } -/* navbar */ - -.sidebar-item-overview a { - font-size: 1.2em; /* Adjust size as needed */ - font-weight: bold; -} - -html[data-theme='light'] .sidebar-item-overview a { - --ifm-menu-color: #000; -} - +/* navbar github link */ .navbar-github-link { width: 32px; height: 32px; @@ -116,8 +103,55 @@ html[data-theme="dark"] .navbar-github-link::before { no-repeat; } -.sidebar-title { - font-size: 1.25em; /* Adjust the size as needed */ - font-weight: bold; - color: #000; +/* Delineate tab blocks */ +.tabs-container { + border: 1px solid var(--ifm-color-primary); /* Adjust the color and thickness as needed */ + border-radius: 5px; /* To give rounded corners */ + padding: 0.5em; /* To add spacing inside the tab */ +} + +/* Docs code bubbles */ +[data-theme="light"] { + --code-link-background: #CFE9FE; + --code-link-text: rgb(21, 50, 59); + + --method-link-background: #CDEFC4; + --method-link-text: rgb(0, 41, 0); + + --scope-link-background: #FBF3E0; + --scope-link-text: rgb(63, 46, 0); + + --event-link-background: #FDDDE3; + --event-link-text: rgb(74, 21, 75); +} + +[data-theme="dark"] { + --code-link-text: white; + --method-link-text: white; + --scope-link-text: white; + --event-link-text: white; + --code-link-background: #1AB9FF50; + --method-link-background: #41B65850; + --scope-link-background: #FCC00350; + --event-link-background: #E3066A50; +} + +a code { + background-color: var(--code-link-background); + color: var(--code-link-text); +} + +a[href^="https://api.slack.com/methods"] > code { + background-color: var(--method-link-background); + color: var(--method-link-text); +} + +a[href^="https://api.slack.com/scopes"] > code { + background-color: var(--scope-link-background); + color: var(--scope-link-text); +} + +a[href^="https://api.slack.com/events"] > code { + background-color: var(--event-link-background); + color: var(--event-link-text); } \ No newline at end of file From 0396b3b1ab8da9daedf91ca894d090f1bc806e70 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Tue, 5 Nov 2024 10:46:20 +0900 Subject: [PATCH 043/282] Run mypy with falcon 4.x (#1199) --- requirements/adapter.txt | 2 +- slack_bolt/adapter/falcon/async_resource.py | 7 ++++--- slack_bolt/adapter/falcon/resource.py | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 1dc0d5d60..4b2cffbbc 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -8,7 +8,7 @@ chalice>=1.28,<2; python_version>"3.6" CherryPy>=18,<19 Django>=3,<6 falcon>=2,<4; python_version<"3.11" -falcon>=3.1.1,<4; python_version>="3.11" +falcon>=3.1.1,<5; python_version>="3.11" fastapi>=0.70.0,<1 Flask>=1,<4 Werkzeug>=2,<4 diff --git a/slack_bolt/adapter/falcon/async_resource.py b/slack_bolt/adapter/falcon/async_resource.py index 0ce219ed1..eece0a323 100644 --- a/slack_bolt/adapter/falcon/async_resource.py +++ b/slack_bolt/adapter/falcon/async_resource.py @@ -1,8 +1,8 @@ from datetime import datetime from http import HTTPStatus -from falcon import version as falcon_version # type: ignore[import-untyped] -from falcon.asgi import Request, Response # type: ignore[import-untyped] +from falcon import version as falcon_version +from falcon.asgi import Request, Response from slack_bolt import BoltResponse from slack_bolt.async_app import AsyncApp from slack_bolt.error import BoltError @@ -41,7 +41,8 @@ async def on_get(self, req: Request, resp: Response): return resp.status = "404" - resp.body = "The page is not found..." + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = "The page is not found..." # type: ignore[assignment] async def on_post(self, req: Request, resp: Response): bolt_req = await self._to_bolt_request(req) diff --git a/slack_bolt/adapter/falcon/resource.py b/slack_bolt/adapter/falcon/resource.py index 45b76146e..baf0f9745 100644 --- a/slack_bolt/adapter/falcon/resource.py +++ b/slack_bolt/adapter/falcon/resource.py @@ -1,7 +1,7 @@ from datetime import datetime from http import HTTPStatus -from falcon import Request, Response, version as falcon_version # type: ignore[import-untyped] +from falcon import Request, Response, version as falcon_version from slack_bolt import BoltResponse from slack_bolt.app import App @@ -35,7 +35,8 @@ def on_get(self, req: Request, resp: Response): return resp.status = "404" - resp.body = "The page is not found..." + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = "The page is not found..." # type: ignore[assignment] def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -51,7 +52,8 @@ def _to_bolt_request(self, req: Request) -> BoltRequest: def _write_response(self, bolt_resp: BoltResponse, resp: Response): if falcon_version.__version__.startswith("2."): - resp.body = bolt_resp.body + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = bolt_resp.body # type: ignore[assignment] else: resp.text = bolt_resp.body From f2bb2d273ddf5c080fe3afc5170dd051ecf22277 Mon Sep 17 00:00:00 2001 From: "slackapi[bot]" <186980925+slackapi[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:04:52 -0800 Subject: [PATCH 044/282] Update config files (#1200) Co-authored-by: slackapi[bot] <186980925+slackapi[bot]@users.noreply.github.com> --- docs/navbarConfig.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/navbarConfig.js b/docs/navbarConfig.js index e9deaab7d..d90e54ea3 100644 --- a/docs/navbarConfig.js +++ b/docs/navbarConfig.js @@ -76,6 +76,10 @@ const navbar = { label: 'API Docs', target: '_self', }, + { + type: 'localeDropdown', + position: 'right', + }, { 'aria-label': 'GitHub Repository', className: 'navbar-github-link', From d455c0d651f5d2e6dfa62029ffe28184379812c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:02:53 +0900 Subject: [PATCH 045/282] chore(deps): bump pytest-runner from 5.2 to 6.0.1 (#1207) Bumps [pytest-runner](https://github.com/pytest-dev/pytest-runner) from 5.2 to 6.0.1. - [Release notes](https://github.com/pytest-dev/pytest-runner/releases) - [Changelog](https://github.com/pytest-dev/pytest-runner/blob/main/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-runner/compare/5.2...v6.0.1) --- updated-dependencies: - dependency-name: pytest-runner dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cd6b5bf63..c5034f0c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "pytest-runner==5.2", "wheel"] +requires = ["setuptools", "pytest-runner==6.0.1", "wheel"] build-backend = "setuptools.build_meta" [project] From d7f3fc773206167fe305c986fa51ec1e0cf2e7f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:03:08 +0900 Subject: [PATCH 046/282] chore(deps): update websockets requirement from <14 to <15 (#1208) Updates the requirements on [websockets](https://github.com/python-websockets/websockets) to permit the latest version. - [Release notes](https://github.com/python-websockets/websockets/releases) - [Commits](https://github.com/python-websockets/websockets/compare/1.0...14.1) --- updated-dependencies: - dependency-name: websockets dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/async.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/async.txt b/requirements/async.txt index 54e62ca94..cd9c368f6 100644 --- a/requirements/async.txt +++ b/requirements/async.txt @@ -1,3 +1,3 @@ # pip install -r requirements/async.txt aiohttp>=3,<4 -websockets<14 +websockets<15 From 1fee66242fe9e13b8ae46dbbc9e1714a9c6968ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:03:22 +0900 Subject: [PATCH 047/282] chore(deps): bump codecov/codecov-action from 4 to 5 (#1209) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 3520eb892..a065a54fa 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -31,7 +31,7 @@ jobs: run: | pytest --cov=./slack_bolt/ --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: fail_ci_if_error: true verbose: true From ffb46b5ff99fee19a29238e3ebf1121cdf6d0a82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:03:40 +0900 Subject: [PATCH 048/282] chore(deps): bump the docusaurus group in /docs with 5 updates (#1210) Bumps the docusaurus group in /docs with 5 updates: | Package | From | To | | --- | --- | --- | | [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) | `3.5.2` | `3.6.3` | | [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) | `3.5.2` | `3.6.3` | | [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) | `3.5.2` | `3.6.3` | | [@docusaurus/module-type-aliases](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-module-type-aliases) | `3.5.2` | `3.6.3` | | [@docusaurus/types](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-types) | `3.5.2` | `3.6.3` | Updates `@docusaurus/core` from 3.5.2 to 3.6.3 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.6.3/packages/docusaurus) Updates `@docusaurus/plugin-client-redirects` from 3.5.2 to 3.6.3 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.6.3/packages/docusaurus-plugin-client-redirects) Updates `@docusaurus/preset-classic` from 3.5.2 to 3.6.3 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.6.3/packages/docusaurus-preset-classic) Updates `@docusaurus/module-type-aliases` from 3.5.2 to 3.6.3 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.6.3/packages/docusaurus-module-type-aliases) Updates `@docusaurus/types` from 3.5.2 to 3.6.3 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.6.3/packages/docusaurus-types) --- updated-dependencies: - dependency-name: "@docusaurus/core" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docusaurus - dependency-name: "@docusaurus/plugin-client-redirects" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docusaurus - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docusaurus - dependency-name: "@docusaurus/module-type-aliases" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: docusaurus - dependency-name: "@docusaurus/types" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: docusaurus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 4895 ++++++++++++++++++++++++++++------------ docs/package.json | 10 +- 2 files changed, 3484 insertions(+), 1421 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 19d882897..90ebb842f 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,9 +8,9 @@ "name": "website", "version": "2024.08.01", "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/plugin-client-redirects": "^3.5.2", - "@docusaurus/preset-classic": "3.5.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/plugin-client-redirects": "^3.6.3", + "@docusaurus/preset-classic": "3.6.3", "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -19,39 +19,39 @@ "react-dom": "^18.0.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.5.2", - "@docusaurus/types": "3.5.2" + "@docusaurus/module-type-aliases": "3.6.3", + "@docusaurus/types": "3.6.3" }, "engines": { "node": ">=20.0" } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", - "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", - "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", - "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -59,9 +59,9 @@ } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", - "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" @@ -88,6 +88,20 @@ "@algolia/cache-common": "4.24.0" } }, + "node_modules/@algolia/client-abtesting": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.15.0.tgz", + "integrity": "sha512-FaEM40iuiv1mAipYyiptP4EyxkJ8qHfowCpEeusdHUC4C7spATJYArD2rX3AxkVeREkDIgYEOuXcwKUbDCr7Nw==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/client-account": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.24.0.tgz", @@ -148,10 +162,23 @@ } }, "node_modules/@algolia/client-common": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.2.4.tgz", - "integrity": "sha512-xNkNJ9Vk1WjxEU/SzcA2vZWeYSiQFQOUS7Akffx8aeAIJIOcmwbpLr2D8JzBEC4QNmNb5KAZOJTrGl1ri9Mclg==", - "peer": true, + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.15.0.tgz", + "integrity": "sha512-IofrVh213VLsDkPoSKMeM9Dshrv28jhDlBDLRcVJQvlL8pzue7PEB1EZ4UoJFYS3NSn7JOcJ/V+olRQzXlJj1w==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.15.0.tgz", + "integrity": "sha512-bDDEQGfFidDi0UQUCbxXOCdphbVAgbVmxvaV75cypBTQkJ+ABx/Npw7LkFGw1FsoVrttlrrQbwjvUB6mLVKs/w==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, "engines": { "node": ">= 14.0.0" } @@ -175,15 +202,29 @@ "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.15.0.tgz", + "integrity": "sha512-wu8GVluiZ5+il8WIRsGKu8VxMK9dAlr225h878GGtpTL6VBvwyJvAyLdZsfFIpY0iN++jiNb31q2C1PlPL+n/A==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/client-search": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.2.4.tgz", - "integrity": "sha512-xlBaro8nU5EvsNsLu8dSsd7jzHVvOVGCOTW4dM6gjRmQDYChzMsF69Tb1OfLaXk7YJ0jHk1rNeccBOsYBtQcIQ==", - "peer": true, + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.15.0.tgz", + "integrity": "sha512-Z32gEMrRRpEta5UqVQA612sLdoqY3AovvUPClDfMxYrbdDAebmGDVPtSogUba1FZ4pP5dx20D3OV3reogLKsRA==", "dependencies": { - "@algolia/client-common": "5.2.4", - "@algolia/requester-browser-xhr": "5.2.4", - "@algolia/requester-node-http": "5.2.4" + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" }, "engines": { "node": ">= 14.0.0" @@ -194,6 +235,20 @@ "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, + "node_modules/@algolia/ingestion": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.15.0.tgz", + "integrity": "sha512-MkqkAxBQxtQ5if/EX2IPqFA7LothghVyvPoRNA/meS2AW2qkHwcxjuiBxv4H6mnAVEPfJlhu9rkdVz9LgCBgJg==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/logger-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.24.0.tgz", @@ -207,6 +262,20 @@ "@algolia/logger-common": "4.24.0" } }, + "node_modules/@algolia/monitoring": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.15.0.tgz", + "integrity": "sha512-QPrFnnGLMMdRa8t/4bs7XilPYnoUXDY8PMQJ1sf9ZFwhUysYYhQNX34/enoO0LBjpoOY6rLpha39YQEFbzgKyQ==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/recommend": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.24.0.tgz", @@ -261,12 +330,11 @@ } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.2.4.tgz", - "integrity": "sha512-ncssmlq86ZnoQ/RH/EEG2KgmBZQnprzx3dZZ+iJrvkbxIi8V9wBWyCgjsuPrKGitzhpnjxZLNlHJZtcps5jaXw==", - "peer": true, + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.15.0.tgz", + "integrity": "sha512-Po/GNib6QKruC3XE+WKP1HwVSfCDaZcXu48kD+gwmtDlqHWKc7Bq9lrS0sNZ456rfCKhXksOmMfUs4wRM/Y96w==", "dependencies": { - "@algolia/client-common": "5.2.4" + "@algolia/client-common": "5.15.0" }, "engines": { "node": ">= 14.0.0" @@ -277,13 +345,23 @@ "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==" }, + "node_modules/@algolia/requester-fetch": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.15.0.tgz", + "integrity": "sha512-rOZ+c0P7ajmccAvpeeNrUmEKoliYFL8aOR5qGW5pFq3oj3Iept7Y5mEtEsOBYsRt6qLnaXn4zUKf+N8nvJpcIw==", + "dependencies": { + "@algolia/client-common": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/requester-node-http": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.2.4.tgz", - "integrity": "sha512-EoLOebO81Dtwuz/hy4onmQAb9dK8fDqyPWMwX017SvGDi3w1h4i6W6//VTO0vKLfXMNpoAKWFi+LBBTLCVtiiw==", - "peer": true, + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.15.0.tgz", + "integrity": "sha512-b1jTpbFf9LnQHEJP5ddDJKE2sAlhYd7EVSOWgzo/27n/SfCoHfqD0VWntnWYD83PnOKvfe8auZ2+xCb0TXotrQ==", "dependencies": { - "@algolia/client-common": "5.2.4" + "@algolia/client-common": "5.15.0" }, "engines": { "node": ">= 14.0.0" @@ -312,11 +390,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -324,28 +403,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -369,50 +448,51 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -429,18 +509,16 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "engines": { @@ -459,12 +537,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", "semver": "^6.3.1" }, "engines": { @@ -497,74 +575,38 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -574,32 +616,32 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -609,13 +651,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -625,186 +667,113 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dependencies": { - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dependencies": { - "color-convert": "^1.9.0" + "@babel/types": "^7.26.0" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dependencies": { - "has-flag": "^3.0.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", - "bin": { - "parser": "bin/babel-parser.js" + "node": ">=6.9.0" }, - "engines": { - "node": ">=6.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -814,11 +783,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -828,13 +797,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -844,12 +813,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -869,10 +838,10 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -880,23 +849,26 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -905,34 +877,26 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.25.9" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -941,48 +905,59 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -991,78 +966,104 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.12.0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1071,12 +1072,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1085,12 +1087,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1099,13 +1101,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1114,198 +1116,12 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1315,12 +1131,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1330,12 +1146,11 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1345,12 +1160,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1360,13 +1175,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1376,12 +1191,11 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1391,11 +1205,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1405,12 +1219,11 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1420,11 +1233,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1434,12 +1247,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1449,13 +1262,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1465,14 +1278,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1482,12 +1295,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1497,12 +1310,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1512,11 +1325,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1526,12 +1339,11 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1541,12 +1353,11 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1556,14 +1367,13 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1573,12 +1383,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1588,12 +1398,11 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1603,13 +1412,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1619,11 +1427,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1633,12 +1441,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1648,14 +1456,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1665,11 +1472,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1693,11 +1500,11 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1707,15 +1514,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1725,11 +1532,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.24.7" + "@babel/plugin-transform-react-jsx": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1739,12 +1546,12 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1754,11 +1561,11 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1768,12 +1575,27 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1783,14 +1605,14 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", - "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -1810,11 +1632,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1824,12 +1646,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1839,11 +1661,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1853,11 +1675,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1867,11 +1689,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1881,14 +1703,15 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz", - "integrity": "sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", + "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-typescript": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1898,11 +1721,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1912,12 +1735,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1927,12 +1750,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1942,12 +1765,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1957,90 +1780,78 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "engines": { @@ -2072,16 +1883,16 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", + "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2091,15 +1902,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", - "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", + "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-typescript": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2108,15 +1919,10 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2125,9 +1931,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.7.tgz", - "integrity": "sha512-eytSX6JLBY6PVAeQa2bFlDx/7Mmln/gaEpsit5a3WEvjGfiIytEsgAwuIXCPM0xvw0v0cJn3ilq0/TvXrW0kgA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.0.tgz", + "integrity": "sha512-YXHu5lN8kJCb1LOb9PgV6pvak43X2h4HvRApcN5SdWeaItQOzfn1hgP6jasD6KWQyJDBxrVmA9o9OivlnNJK/w==", "dependencies": { "core-js-pure": "^3.30.2", "regenerator-runtime": "^0.14.0" @@ -2137,31 +1943,28 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2170,13 +1973,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2191,40 +1993,1096 @@ "node": ">=0.1.90" } }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "node_modules/@csstools/cascade-layer-name-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.4.tgz", + "integrity": "sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docsearch/css": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.1.tgz", - "integrity": "sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==" - }, - "node_modules/@docsearch/react": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.1.tgz", - "integrity": "sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==", - "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.1", - "algoliasearch": "^4.19.1" + "node": ">=18" }, "peerDependencies": { - "@types/react": ">= 16.8.0 < 19.0.0", - "react": ">= 16.8.0 < 19.0.0", - "react-dom": ">= 16.8.0 < 19.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", + "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" }, - "react": { + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.0.tgz", + "integrity": "sha512-X69PmFOrjTZfN5ijxtI8hZ9kRADFSLrmmQ6hgDJ272Il049WGKpDY64KhrFm/7rbWve0z81QepawzjkKlqkNGw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.6.tgz", + "integrity": "sha512-S/IjXqTHdpI4EtzGoNCHfqraXF37x12ZZHA1Lk7zoT5pm2lMjFuqhX/89L7dqX4CcMacKK+6ZCs5TmEGb/+wKw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/color-helpers": "^5.0.1", + "@csstools/css-calc": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", + "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.1.tgz", + "integrity": "sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-cascade-layers/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.6.tgz", + "integrity": "sha512-EcvXfC60cTIumzpsxWuvVjb7rsJEHPvqn3jeMEBUaE3JSc4FRuP7mEQ+1eicxWmIrs3FtzMH9gR3sgA5TH+ebQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-color-mix-function": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.6.tgz", + "integrity": "sha512-jVKdJn4+JkASYGhyPO+Wa5WXSx1+oUgaXb3JsjJn/BlrtFh5zjocCY7pwWi0nuP24V1fY7glQsxEYcYNy0dMFg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-content-alt-text": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.4.tgz", + "integrity": "sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-exponential-functions": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.5.tgz", + "integrity": "sha512-mi8R6dVfA2nDoKM3wcEi64I8vOYEgQVtVKCfmLHXupeLpACfGAided5ddMt5f+CnEodNu4DifuVwb0I6fQDGGQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-4.0.0.tgz", + "integrity": "sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-gamut-mapping": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.6.tgz", + "integrity": "sha512-0ke7fmXfc8H+kysZz246yjirAH6JFhyX9GTlyRnM0exHO80XcA9zeJpy5pOp5zo/AZiC/q5Pf+Hw7Pd6/uAoYA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-gradients-interpolation-method": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.6.tgz", + "integrity": "sha512-Itrbx6SLUzsZ6Mz3VuOlxhbfuyLTogG5DwEF1V8dAi24iMuvQPIHd7Ti+pNDp7j6WixndJGZaoNR0f9VSzwuTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.6.tgz", + "integrity": "sha512-927Pqy3a1uBP7U8sTfaNdZVB0mNXzIrJO/GZ8us9219q9n06gOqCdfZ0E6d1P66Fm0fYHvxfDbfcUuwAn5UwhQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.0.tgz", + "integrity": "sha512-9QT5TDGgx7wD3EEMN3BSUG6ckb6Eh5gSPT5kZoVtUuAonfPmLDJyPhqR4ntPpMYhUKAMVKAg3I/AgzqHMSeLhA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-initial": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.0.tgz", + "integrity": "sha512-dv2lNUKR+JV+OOhZm9paWzYBXOCi+rJPqJ2cJuhh9xd8USVrd0cBEPczla81HNOyThMQWeCcdln3gZkQV2kYxA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.1.tgz", + "integrity": "sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@csstools/postcss-light-dark-function": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.7.tgz", + "integrity": "sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-float-and-clear": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-3.0.0.tgz", + "integrity": "sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-overflow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overflow/-/postcss-logical-overflow-2.0.0.tgz", + "integrity": "sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-overscroll-behavior": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overscroll-behavior/-/postcss-logical-overscroll-behavior-2.0.0.tgz", + "integrity": "sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-resize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-3.0.0.tgz", + "integrity": "sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-viewport-units": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.3.tgz", + "integrity": "sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-media-minmax": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.5.tgz", + "integrity": "sha512-sdh5i5GToZOIAiwhdntRWv77QDtsxP2r2gXW/WbLSCoLr00KTq/yiF1qlQ5XX2+lmiFa8rATKMcbwl3oXDMNew==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/media-query-list-parser": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.4.tgz", + "integrity": "sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/media-query-list-parser": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-4.0.0.tgz", + "integrity": "sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.0.tgz", + "integrity": "sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.6.tgz", + "integrity": "sha512-Hptoa0uX+XsNacFBCIQKTUBrFKDiplHan42X73EklG6XmQLG7/aIvxoNhvZ7PvOWMt67Pw3bIlUY2nD6p5vL8A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz", + "integrity": "sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-random-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-1.0.1.tgz", + "integrity": "sha512-Ab/tF8/RXktQlFwVhiC70UNfpFQRhtE5fQQoP2pO+KCPGLsLdWFiOuHgSRtBOqEshCVAzR4H6o38nhvRZq8deA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-relative-color-syntax": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.6.tgz", + "integrity": "sha512-yxP618Xb+ji1I624jILaYM62uEmZcmbdmFoZHoaThw896sq0vU39kqTTF+ZNic9XyPtPMvq0vyvbgmHaszq8xg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-scope-pseudo-class": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-4.0.1.tgz", + "integrity": "sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@csstools/postcss-sign-functions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.0.tgz", + "integrity": "sha512-SLcc20Nujx/kqbSwDmj6oaXgpy3UjFhBy1sfcqPgDkHfOIfUtUVH7OXO+j7BU4v/At5s61N5ZX6shvgPwluhsA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.5.tgz", + "integrity": "sha512-G6SJ6hZJkhxo6UZojVlLo14MohH4J5J7z8CRBrxxUYy9JuZiIqUo5TBYyDGcE0PLdzpg63a7mHSJz3VD+gMwqw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.1.tgz", + "integrity": "sha512-xPZIikbx6jyzWvhms27uugIc0I4ykH4keRvoa3rxX5K7lEhkbd54rjj/dv60qOCTisoS+3bmwJTeyV1VNBrXaw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/color-helpers": "^5.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.5.tgz", + "integrity": "sha512-/YQThYkt5MLvAmVu7zxjhceCYlKrYddK6LEmK5I4ojlS6BmO9u2yO4+xjXzu2+NPYmHSTtP4NFSamBCMmJ1NJA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz", + "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/utilities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", + "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.0.tgz", + "integrity": "sha512-pieeipSOW4sQ0+bE5UFC51AOZp9NGxg89wAlZ1BAQFaiRAGK1IKUaPQ0UGZeNctJXyqZ1UvBtOQh2HH+U5GtmA==" + }, + "node_modules/@docsearch/react": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.0.tgz", + "integrity": "sha512-WnFK720+iwTVt94CxY3u+FgX6exb3BfN5kE9xUY6uuAH/9W/UFboBZFLlrw/zxFRHoHZCOXRtOylsXF+6LHI+Q==", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.0", + "algoliasearch": "^5.12.0" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { "optional": true }, "react-dom": { @@ -2235,58 +3093,169 @@ } } }, - "node_modules/@docusaurus/core": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.5.2.tgz", - "integrity": "sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==", + "node_modules/@docsearch/react/node_modules/@algolia/client-analytics": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.15.0.tgz", + "integrity": "sha512-lho0gTFsQDIdCwyUKTtMuf9nCLwq9jOGlLGIeQGKDxXF7HbiAysFIu5QW/iQr1LzMgDyM9NH7K98KY+BiIFriQ==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docsearch/react/node_modules/@algolia/client-personalization": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.15.0.tgz", + "integrity": "sha512-LfaZqLUWxdYFq44QrasCDED5bSYOswpQjSiIL7Q5fYlefAAUO95PzBPKCfUhSwhb4rKxigHfDkd81AvEicIEoA==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docsearch/react/node_modules/@algolia/recommend": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.15.0.tgz", + "integrity": "sha512-5eupMwSqMLDObgSMF0XG958zR6GJP3f7jHDQ3/WlzCM9/YIJiWIUoJFGsko9GYsA5xbLDHE/PhWtq4chcCdaGQ==", + "dependencies": { + "@algolia/client-common": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docsearch/react/node_modules/algoliasearch": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.15.0.tgz", + "integrity": "sha512-Yf3Swz1s63hjvBVZ/9f2P1Uu48GjmjCN+Esxb6MAONMGtZB1fRX8/S1AhUTtsuTlcGovbYLxpHgc7wEzstDZBw==", "dependencies": { - "@babel/core": "^7.23.3", - "@babel/generator": "^7.23.3", + "@algolia/client-abtesting": "5.15.0", + "@algolia/client-analytics": "5.15.0", + "@algolia/client-common": "5.15.0", + "@algolia/client-insights": "5.15.0", + "@algolia/client-personalization": "5.15.0", + "@algolia/client-query-suggestions": "5.15.0", + "@algolia/client-search": "5.15.0", + "@algolia/ingestion": "1.15.0", + "@algolia/monitoring": "1.15.0", + "@algolia/recommend": "5.15.0", + "@algolia/requester-browser-xhr": "5.15.0", + "@algolia/requester-fetch": "5.15.0", + "@algolia/requester-node-http": "5.15.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@docusaurus/babel": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.6.3.tgz", + "integrity": "sha512-7dW9Hat9EHYCVicFXYA4hjxBY38+hPuCURL8oRF9fySRm7vzNWuEOghA1TXcykuXZp0HLG2td4RhDxCvGG7tNw==", + "dependencies": { + "@babel/core": "^7.25.9", + "@babel/generator": "^7.25.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@babel/runtime-corejs3": "^7.22.6", - "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.5.2", - "@docusaurus/logger": "3.5.2", - "@docusaurus/mdx-loader": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", - "autoprefixer": "^10.4.14", - "babel-loader": "^9.1.3", + "@babel/plugin-transform-runtime": "^7.25.9", + "@babel/preset-env": "^7.25.9", + "@babel/preset-react": "^7.25.9", + "@babel/preset-typescript": "^7.25.9", + "@babel/runtime": "^7.25.9", + "@babel/runtime-corejs3": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@docusaurus/logger": "3.6.3", + "@docusaurus/utils": "3.6.3", "babel-plugin-dynamic-import-node": "^2.3.3", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@docusaurus/bundler": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.6.3.tgz", + "integrity": "sha512-47JLuc8D4wA+6VOvmMd5fUC9rFppBQpQOnxDYiVXffm/DeV/wmm3sbpNd5Y+O+G2+nevLTRnvCm/qyancv0Y3A==", + "dependencies": { + "@babel/core": "^7.25.9", + "@docusaurus/babel": "3.6.3", + "@docusaurus/cssnano-preset": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils": "3.6.3", + "babel-loader": "^9.2.1", + "clean-css": "^5.3.2", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.8.1", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", + "file-loader": "^6.2.0", + "html-minifier-terser": "^7.2.0", + "mini-css-extract-plugin": "^2.9.1", + "null-loader": "^4.0.1", + "postcss": "^8.4.26", + "postcss-loader": "^7.3.3", + "postcss-preset-env": "^10.1.0", + "react-dev-utils": "^12.0.1", + "terser-webpack-plugin": "^5.3.9", + "tslib": "^2.6.0", + "url-loader": "^4.1.1", + "webpack": "^5.95.0", + "webpackbar": "^6.0.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "@docusaurus/faster": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/faster": { + "optional": true + } + } + }, + "node_modules/@docusaurus/core": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.6.3.tgz", + "integrity": "sha512-xL7FRY9Jr5DWqB6pEnqgKqcMPJOX5V0pgWXi5lCiih11sUBmcFKM7c3+GyxcVeeWFxyYSDP3grLTWqJoP4P9Vw==", + "dependencies": { + "@docusaurus/babel": "3.6.3", + "@docusaurus/bundler": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/mdx-loader": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", - "clean-css": "^5.3.2", "cli-table3": "^0.6.3", "combine-promises": "^1.1.0", "commander": "^5.1.0", - "copy-webpack-plugin": "^11.0.0", "core-js": "^3.31.1", - "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", "eval": "^0.1.8", - "file-loader": "^6.2.0", "fs-extra": "^11.1.1", - "html-minifier-terser": "^7.2.0", "html-tags": "^3.3.1", - "html-webpack-plugin": "^5.5.3", + "html-webpack-plugin": "^5.6.0", "leven": "^3.1.0", "lodash": "^4.17.21", - "mini-css-extract-plugin": "^2.7.6", "p-map": "^4.0.0", - "postcss": "^8.4.26", - "postcss-loader": "^7.3.3", "prompts": "^2.4.2", "react-dev-utils": "^12.0.1", "react-helmet-async": "^1.3.0", @@ -2297,17 +3266,14 @@ "react-router-dom": "^5.3.4", "rtl-detect": "^1.0.4", "semver": "^7.5.4", - "serve-handler": "^6.1.5", + "serve-handler": "^6.1.6", "shelljs": "^0.8.5", - "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "update-notifier": "^6.0.2", - "url-loader": "^4.1.1", - "webpack": "^5.88.1", - "webpack-bundle-analyzer": "^4.9.0", - "webpack-dev-server": "^4.15.1", - "webpack-merge": "^5.9.0", - "webpackbar": "^5.0.2" + "webpack": "^5.95.0", + "webpack-bundle-analyzer": "^4.10.2", + "webpack-dev-server": "^4.15.2", + "webpack-merge": "^6.0.1" }, "bin": { "docusaurus": "bin/docusaurus.mjs" @@ -2321,10 +3287,23 @@ "react-dom": "^18.0.0" } }, + "node_modules/@docusaurus/core/node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz", - "integrity": "sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.3.tgz", + "integrity": "sha512-qP7SXrwZ+23GFJdPN4aIHQrZW+oH/7tzwEuc/RNL0+BdZdmIjYQqUxdXsjE4lFxLNZjj0eUrSNYIS6xwfij+5Q==", "dependencies": { "cssnano-preset-advanced": "^6.1.2", "postcss": "^8.4.38", @@ -2336,9 +3315,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.5.2.tgz", - "integrity": "sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.6.3.tgz", + "integrity": "sha512-xSubJixcNyMV9wMV4q0s47CBz3Rlc5jbcCCuij8pfQP8qn/DIpt0ks8W6hQWzHAedg/J/EwxxUOUrnEoKzJo8g==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -2348,13 +3327,13 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz", - "integrity": "sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.6.3.tgz", + "integrity": "sha512-3iJdiDz9540ppBseeI93tWTDtUGVkxzh59nMq4ignylxMuXBLK8dFqVeaEor23v1vx6TrGKZ2FuLaTB+U7C0QQ==", "dependencies": { - "@docusaurus/logger": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "@docusaurus/logger": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2386,11 +3365,11 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz", - "integrity": "sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.3.tgz", + "integrity": "sha512-MjaXX9PN/k5ugNvfRZdWyKWq4FsrhN4LEXaj0pEmMebJuBNlFeGyKQUa9DRhJHpadNaiMLrbo9m3U7Ig5YlsZg==", "dependencies": { - "@docusaurus/types": "3.5.2", + "@docusaurus/types": "3.6.3", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2404,15 +3383,15 @@ } }, "node_modules/@docusaurus/plugin-client-redirects": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.5.2.tgz", - "integrity": "sha512-GMU0ZNoVG1DEsZlBbwLPdh0iwibrVZiRfmdppvX17SnByCVP74mb/Nne7Ss7ALgxQLtM4IHbXi8ij90VVjAJ+Q==", - "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/logger": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.6.3.tgz", + "integrity": "sha512-fQDCxoJCO1jXNQGQmhgYoX3Yx+Z2xSbrLf3PBET6pHnsRk6gGW/VuCHcfQuZlJzbTxN0giQ5u3XcQQ/LzXftJA==", + "dependencies": { + "@docusaurus/core": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "eta": "^2.2.0", "fs-extra": "^11.1.1", "lodash": "^4.17.21", @@ -2427,18 +3406,18 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz", - "integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==", - "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/logger": "3.5.2", - "@docusaurus/mdx-loader": "3.5.2", - "@docusaurus/theme-common": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.3.tgz", + "integrity": "sha512-k0ogWwwJU3pFRFfvW1kRVHxzf2DutLGaaLjAnHVEU6ju+aRP0Z5ap/13DHyPOfHeE4WKpn/M0TqjdwZAcY3kAw==", + "dependencies": { + "@docusaurus/core": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/mdx-loader": "3.6.3", + "@docusaurus/theme-common": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -2460,19 +3439,19 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz", - "integrity": "sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==", - "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/logger": "3.5.2", - "@docusaurus/mdx-loader": "3.5.2", - "@docusaurus/module-type-aliases": "3.5.2", - "@docusaurus/theme-common": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.3.tgz", + "integrity": "sha512-r2wS8y/fsaDcxkm20W5bbYJFPzdWdEaTWVYjNxlHlcmX086eqQR1Fomlg9BHTJ0dLXPzAlbC8EN4XqMr3QzNCQ==", + "dependencies": { + "@docusaurus/core": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/mdx-loader": "3.6.3", + "@docusaurus/module-type-aliases": "3.6.3", + "@docusaurus/theme-common": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2491,15 +3470,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz", - "integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==", - "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/mdx-loader": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.3.tgz", + "integrity": "sha512-eHrmTgjgLZsuqfsYr5X2xEwyIcck0wseSofWrjTwT9FLOWp+KDmMAuVK+wRo7sFImWXZk3oV/xX/g9aZrhD7OA==", + "dependencies": { + "@docusaurus/core": "3.6.3", + "@docusaurus/mdx-loader": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2513,13 +3492,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz", - "integrity": "sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.6.3.tgz", + "integrity": "sha512-zB9GXfIZNPRfzKnNjU6xGVrqn9bPXuGhpjgsuc/YtcTDjnjhasg38NdYd5LEqXex5G/zIorQgWB3n6x/Ut62vQ==", "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils": "3.5.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils": "3.6.3", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2533,13 +3512,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz", - "integrity": "sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.3.tgz", + "integrity": "sha512-rCDNy1QW8Dag7nZq67pcum0bpFLrwvxJhYuVprhFh8BMBDxV0bY+bAkGHbSf68P3Bk9C3hNOAXX1srGLIDvcTA==", "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "tslib": "^2.6.0" }, "engines": { @@ -2551,13 +3530,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz", - "integrity": "sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.3.tgz", + "integrity": "sha512-+OyDvhM6rqVkQOmLVkQWVJAizEEfkPzVWtIHXlWPOCFGK9X4/AWeBSrU0WG4iMg9Z4zD4YDRrU+lvI4s6DSC+w==", "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2570,13 +3549,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz", - "integrity": "sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.3.tgz", + "integrity": "sha512-1M6UPB13gWUtN2UHX083/beTn85PlRI9ABItTl/JL1FJ5dJTWWFXXsHf9WW/6hrVwthwTeV/AGbGKvLKV+IlCA==", "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "tslib": "^2.6.0" }, "engines": { @@ -2588,16 +3567,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz", - "integrity": "sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==", - "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/logger": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.3.tgz", + "integrity": "sha512-94qOO4M9Fwv9KfVQJsgbe91k+fPJ4byf1L3Ez8TUa6TAFPo/BrLwQ80zclHkENlL1824TuxkcMKv33u6eydQCg==", + "dependencies": { + "@docusaurus/core": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2611,23 +3590,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz", - "integrity": "sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==", - "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/plugin-content-blog": "3.5.2", - "@docusaurus/plugin-content-docs": "3.5.2", - "@docusaurus/plugin-content-pages": "3.5.2", - "@docusaurus/plugin-debug": "3.5.2", - "@docusaurus/plugin-google-analytics": "3.5.2", - "@docusaurus/plugin-google-gtag": "3.5.2", - "@docusaurus/plugin-google-tag-manager": "3.5.2", - "@docusaurus/plugin-sitemap": "3.5.2", - "@docusaurus/theme-classic": "3.5.2", - "@docusaurus/theme-common": "3.5.2", - "@docusaurus/theme-search-algolia": "3.5.2", - "@docusaurus/types": "3.5.2" + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.6.3.tgz", + "integrity": "sha512-VHSYWROT3flvNNI1SrnMOtW1EsjeHNK9dhU6s9eY5hryZe79lUqnZJyze/ymDe2LXAqzyj6y5oYvyBoZZk6ErA==", + "dependencies": { + "@docusaurus/core": "3.6.3", + "@docusaurus/plugin-content-blog": "3.6.3", + "@docusaurus/plugin-content-docs": "3.6.3", + "@docusaurus/plugin-content-pages": "3.6.3", + "@docusaurus/plugin-debug": "3.6.3", + "@docusaurus/plugin-google-analytics": "3.6.3", + "@docusaurus/plugin-google-gtag": "3.6.3", + "@docusaurus/plugin-google-tag-manager": "3.6.3", + "@docusaurus/plugin-sitemap": "3.6.3", + "@docusaurus/theme-classic": "3.6.3", + "@docusaurus/theme-common": "3.6.3", + "@docusaurus/theme-search-algolia": "3.6.3", + "@docusaurus/types": "3.6.3" }, "engines": { "node": ">=18.0" @@ -2638,26 +3617,27 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz", - "integrity": "sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==", - "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/mdx-loader": "3.5.2", - "@docusaurus/module-type-aliases": "3.5.2", - "@docusaurus/plugin-content-blog": "3.5.2", - "@docusaurus/plugin-content-docs": "3.5.2", - "@docusaurus/plugin-content-pages": "3.5.2", - "@docusaurus/theme-common": "3.5.2", - "@docusaurus/theme-translations": "3.5.2", - "@docusaurus/types": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.6.3.tgz", + "integrity": "sha512-1RRLK1tSArI2c00qugWYO3jRocjOZwGF1mBzPPylDVRwWCS/rnWWR91ChdbbaxIupRJ+hX8ZBYrwr5bbU0oztQ==", + "dependencies": { + "@docusaurus/core": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/mdx-loader": "3.6.3", + "@docusaurus/module-type-aliases": "3.6.3", + "@docusaurus/plugin-content-blog": "3.6.3", + "@docusaurus/plugin-content-docs": "3.6.3", + "@docusaurus/plugin-content-pages": "3.6.3", + "@docusaurus/theme-common": "3.6.3", + "@docusaurus/theme-translations": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.44", + "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.26", @@ -2677,14 +3657,14 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.5.2.tgz", - "integrity": "sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==", - "dependencies": { - "@docusaurus/mdx-loader": "3.5.2", - "@docusaurus/module-type-aliases": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.6.3.tgz", + "integrity": "sha512-b8ZkhczXHDxWWyvz+YJy4t/PlPbEogTTbgnHoflYnH7rmRtyoodTsu8WVM12la5LmlMJBclBXFl29OH8kPE7gg==", + "dependencies": { + "@docusaurus/mdx-loader": "3.6.3", + "@docusaurus/module-type-aliases": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2704,18 +3684,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz", - "integrity": "sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.3.tgz", + "integrity": "sha512-rt+MGCCpYgPyWCGXtbxlwFbTSobu15jWBTPI2LHsHNa5B0zSmOISX6FWYAPt5X1rNDOqMGM0FATnh7TBHRohVA==", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.5.2", - "@docusaurus/logger": "3.5.2", - "@docusaurus/plugin-content-docs": "3.5.2", - "@docusaurus/theme-common": "3.5.2", - "@docusaurus/theme-translations": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-validation": "3.5.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/logger": "3.6.3", + "@docusaurus/plugin-content-docs": "3.6.3", + "@docusaurus/theme-common": "3.6.3", + "@docusaurus/theme-translations": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-validation": "3.6.3", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2734,9 +3714,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz", - "integrity": "sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.6.3.tgz", + "integrity": "sha512-Gb0regclToVlngSIIwUCtBMQBq48qVUaN1XQNKW4XwlsgUyk0vP01LULdqbem7czSwIeBAFXFoORJ0RPX7ht/w==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -2746,9 +3726,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz", - "integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.6.3.tgz", + "integrity": "sha512-xD9oTGDrouWzefkhe9ogB2fDV96/82cRpNGx2HIvI5L87JHNhQVIWimQ/3JIiiX/TEd5S9s+VO6FFguwKNRVow==", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -2757,7 +3737,7 @@ "joi": "^17.9.2", "react-helmet-async": "^1.3.0", "utility-types": "^3.10.0", - "webpack": "^5.88.1", + "webpack": "^5.95.0", "webpack-merge": "^5.9.0" }, "peerDependencies": { @@ -2766,12 +3746,13 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.5.2.tgz", - "integrity": "sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.6.3.tgz", + "integrity": "sha512-0R/FR3bKVl4yl8QwbL4TYFfR+OXBRpVUaTJdENapBGR3YMwfM6/JnhGilWQO8AOwPJGtGoDK7ib8+8UF9f3OZQ==", "dependencies": { - "@docusaurus/logger": "3.5.2", - "@docusaurus/utils-common": "3.5.2", + "@docusaurus/logger": "3.6.3", + "@docusaurus/types": "3.6.3", + "@docusaurus/utils-common": "3.6.3", "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2793,43 +3774,28 @@ }, "engines": { "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/types": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/types": { - "optional": true - } } }, "node_modules/@docusaurus/utils-common": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.5.2.tgz", - "integrity": "sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.6.3.tgz", + "integrity": "sha512-v4nKDaANLgT3pMBewHYEMAl/ufY0LkXao1QkFWzI5huWFOmNQ2UFzv2BiKeHX5Ownis0/w6cAyoxPhVdDonlSQ==", "dependencies": { + "@docusaurus/types": "3.6.3", "tslib": "^2.6.0" }, "engines": { "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/types": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/types": { - "optional": true - } } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz", - "integrity": "sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.6.3.tgz", + "integrity": "sha512-bhEGGiN5BE38h21vjqD70Gxg++j+PfYVddDUE5UFvLDup68QOcpD33CLr+2knPorlxRbEaNfz6HQDUMQ3HuqKw==", "dependencies": { - "@docusaurus/logger": "3.5.2", - "@docusaurus/utils": "3.5.2", - "@docusaurus/utils-common": "3.5.2", + "@docusaurus/logger": "3.6.3", + "@docusaurus/utils": "3.6.3", + "@docusaurus/utils-common": "3.6.3", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -3417,10 +4383,28 @@ "@types/ms": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", @@ -3683,9 +4667,9 @@ } }, "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dependencies": { "@types/yargs-parser": "*" } @@ -3873,9 +4857,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "bin": { "acorn": "bin/acorn" }, @@ -3883,14 +4867,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -3995,9 +4971,9 @@ } }, "node_modules/algoliasearch-helper": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.4.tgz", - "integrity": "sha512-fvBCywguW9f+939S6awvRMstqMF1XXcd2qs1r1aGqL/PJ1go/DqN06tWmDVmhCDqBJanm++imletrQWf0G2S1g==", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.5.tgz", + "integrity": "sha512-lWvhdnc+aKOKx8jyA3bsdEgHzm/sglC4cYdMG4xSQyRiPLJVJtH/IVYZG3Hp6PkTEhQqhyVYkeP9z2IlcHJsWw==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4066,6 +5042,31 @@ "node": ">=8" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -4187,9 +5188,9 @@ } }, "node_modules/babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", "dependencies": { "find-cache-dir": "^4.0.0", "schema-utils": "^4.0.0" @@ -4232,12 +5233,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -4392,9 +5393,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "funding": [ { "type": "opencollective", @@ -4410,10 +5411,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4518,9 +5519,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001655", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", - "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", "funding": [ { "type": "opencollective", @@ -4936,9 +5937,12 @@ } }, "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } }, "node_modules/content-disposition": { "version": "0.5.2", @@ -5059,11 +6063,11 @@ } }, "node_modules/core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", + "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", "dependencies": { - "browserslist": "^4.23.0" + "browserslist": "^4.24.2" }, "funding": { "type": "opencollective", @@ -5071,9 +6075,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.1.tgz", - "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.39.0.tgz", + "integrity": "sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5148,6 +6152,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/css-blank-pseudo": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz", + "integrity": "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", @@ -5159,6 +6199,65 @@ "postcss": "^8.0.9" } }, + "node_modules/css-has-pseudo": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.1.tgz", + "integrity": "sha512-EOcoyJt+OsuKfCADgLT7gADZI5jMzIe/AeI6MeAYKiFBDmNmM7kk46DtSfMj5AohUJisqVzopBpnQTlvbyaBWg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-has-pseudo/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/css-loader": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", @@ -5236,6 +6335,27 @@ } } }, + "node_modules/css-prefers-color-scheme": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-10.0.0.tgz", + "integrity": "sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -5274,6 +6394,21 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssdb": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.2.1.tgz", + "integrity": "sha512-KwEPys7lNsC8OjASI8RrmwOYYDcm0JOW9zQhcV83ejYcQkirTEyeAGui8aO2F5PiS6SLpxuTzl6qlMElIdsgIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ] + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -5790,9 +6925,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" + "version": "1.5.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", + "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -5885,9 +7020,9 @@ "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==" }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -6245,14 +7380,6 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "dependencies": { - "punycode": "^1.3.2" - } - }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -6292,7 +7419,29 @@ "xml-js": "^1.6.11" }, "engines": { - "node": ">=0.4.0" + "node": ">=0.4.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" } }, "node_modules/file-loader": { @@ -7581,9 +8730,9 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.44", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.44.tgz", - "integrity": "sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==", + "version": "0.2.0-alpha.45", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz", + "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==", "engines": { "node": ">=12" } @@ -8005,14 +9154,14 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -10409,9 +11558,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -10596,6 +11745,70 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/null-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", + "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/null-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/null-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/null-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/null-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -10875,11 +12088,11 @@ } }, "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dependencies": { - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "parse5": "^7.0.0" }, "funding": { @@ -10964,9 +12177,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -11070,68 +12283,326 @@ "url": "https://opencollective.com/postcss/" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz", + "integrity": "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.6.tgz", + "integrity": "sha512-wLXvm8RmLs14Z2nVpB4CWlnvaWPRcOZFltJSlcbYwSJ1EDZKsKDhPKIMecCnuU054KSmlmubkqczmm6qBPCBhA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-10.0.0.tgz", + "integrity": "sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-10.0.0.tgz", + "integrity": "sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-colormin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-convert-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-custom-media": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.5.tgz", + "integrity": "sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/cascade-layer-name-parser": "^2.0.4", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/media-query-list-parser": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-properties": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.4.tgz", + "integrity": "sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/cascade-layer-name-parser": "^2.0.4", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.4.tgz", + "integrity": "sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" }, { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "opencollective", + "url": "https://opencollective.com/csstools" } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "@csstools/cascade-layer-name-parser": "^2.0.4", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "postcss-selector-parser": "^7.0.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dependencies": { - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" + "node": ">=4" } }, - "node_modules/postcss-colormin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", - "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "node_modules/postcss-dir-pseudo-class": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz", + "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" + "postcss-selector-parser": "^7.0.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": ">=18" }, "peerDependencies": { - "postcss": "^8.4.31" + "postcss": "^8.4" } }, - "node_modules/postcss-convert-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", - "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" + "node": ">=4" } }, "node_modules/postcss-discard-comments": { @@ -11192,6 +12663,186 @@ "postcss": "^8.4.31" } }, + "node_modules/postcss-double-position-gradients": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.0.tgz", + "integrity": "sha512-JkIGah3RVbdSEIrcobqj4Gzq0h53GG4uqDPsho88SgY84WnpkTpI0k50MFK/sX7XqVisZ6OqUfFnoUO6m1WWdg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-10.0.1.tgz", + "integrity": "sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-focus-within": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-9.0.1.tgz", + "integrity": "sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-6.0.0.tgz", + "integrity": "sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-image-set-function": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-7.0.0.tgz", + "integrity": "sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-lab-function": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.6.tgz", + "integrity": "sha512-HPwvsoK7C949vBZ+eMyvH2cQeMr3UREoHvbtra76/UhDuiViZH6pir+z71UaJQohd7VDSVUdR6TkWYKExEc9aQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/css-color-parser": "^3.0.6", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/postcss-loader": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", @@ -11213,6 +12864,30 @@ "webpack": "^5.0.0" } }, + "node_modules/postcss-logical": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.0.0.tgz", + "integrity": "sha512-HpIdsdieClTjXLOyYdUPAX/XQASNIwdKt5hoZW08ZOAiI+tbV0ta1oclkpVkW5ANU+xJvk3KkA0FejkjGLXUkg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/postcss-merge-idents": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", @@ -11331,48 +13006,152 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "node_modules/postcss-modules-local-by-default": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz", + "integrity": "sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", + "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" + "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=18" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.4" } }, - "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" + "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz", + "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=18" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss-selector-parser": "^7.0.0" } }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dependencies": { - "icss-utils": "^5.0.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=4" } }, "node_modules/postcss-normalize-charset": { @@ -11499,6 +13278,27 @@ "postcss": "^8.4.31" } }, + "node_modules/postcss-opacity-percentage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-3.0.0.tgz", + "integrity": "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/postcss-ordered-values": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", @@ -11514,6 +13314,184 @@ "postcss": "^8.4.31" } }, + "node_modules/postcss-overflow-shorthand": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-6.0.0.tgz", + "integrity": "sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-10.0.0.tgz", + "integrity": "sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-preset-env": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.1.1.tgz", + "integrity": "sha512-wqqsnBFD6VIwcHHRbhjTOcOi4qRVlB26RwSr0ordPj7OubRRxdWebv/aLjKLRR8zkZrbxZyuus03nOIgC5elMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/postcss-cascade-layers": "^5.0.1", + "@csstools/postcss-color-function": "^4.0.6", + "@csstools/postcss-color-mix-function": "^3.0.6", + "@csstools/postcss-content-alt-text": "^2.0.4", + "@csstools/postcss-exponential-functions": "^2.0.5", + "@csstools/postcss-font-format-keywords": "^4.0.0", + "@csstools/postcss-gamut-mapping": "^2.0.6", + "@csstools/postcss-gradients-interpolation-method": "^5.0.6", + "@csstools/postcss-hwb-function": "^4.0.6", + "@csstools/postcss-ic-unit": "^4.0.0", + "@csstools/postcss-initial": "^2.0.0", + "@csstools/postcss-is-pseudo-class": "^5.0.1", + "@csstools/postcss-light-dark-function": "^2.0.7", + "@csstools/postcss-logical-float-and-clear": "^3.0.0", + "@csstools/postcss-logical-overflow": "^2.0.0", + "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", + "@csstools/postcss-logical-resize": "^3.0.0", + "@csstools/postcss-logical-viewport-units": "^3.0.3", + "@csstools/postcss-media-minmax": "^2.0.5", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.4", + "@csstools/postcss-nested-calc": "^4.0.0", + "@csstools/postcss-normalize-display-values": "^4.0.0", + "@csstools/postcss-oklab-function": "^4.0.6", + "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/postcss-random-function": "^1.0.1", + "@csstools/postcss-relative-color-syntax": "^3.0.6", + "@csstools/postcss-scope-pseudo-class": "^4.0.1", + "@csstools/postcss-sign-functions": "^1.1.0", + "@csstools/postcss-stepped-value-functions": "^4.0.5", + "@csstools/postcss-text-decoration-shorthand": "^4.0.1", + "@csstools/postcss-trigonometric-functions": "^4.0.5", + "@csstools/postcss-unset-value": "^4.0.0", + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.1", + "css-blank-pseudo": "^7.0.1", + "css-has-pseudo": "^7.0.1", + "css-prefers-color-scheme": "^10.0.0", + "cssdb": "^8.2.1", + "postcss-attribute-case-insensitive": "^7.0.1", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^7.0.6", + "postcss-color-hex-alpha": "^10.0.0", + "postcss-color-rebeccapurple": "^10.0.0", + "postcss-custom-media": "^11.0.5", + "postcss-custom-properties": "^14.0.4", + "postcss-custom-selectors": "^8.0.4", + "postcss-dir-pseudo-class": "^9.0.1", + "postcss-double-position-gradients": "^6.0.0", + "postcss-focus-visible": "^10.0.1", + "postcss-focus-within": "^9.0.1", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^6.0.0", + "postcss-image-set-function": "^7.0.0", + "postcss-lab-function": "^7.0.6", + "postcss-logical": "^8.0.0", + "postcss-nesting": "^13.0.1", + "postcss-opacity-percentage": "^3.0.0", + "postcss-overflow-shorthand": "^6.0.0", + "postcss-page-break": "^3.0.4", + "postcss-place": "^10.0.0", + "postcss-pseudo-class-any-link": "^10.0.1", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^8.0.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz", + "integrity": "sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-reduce-idents": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", @@ -11557,10 +13535,54 @@ "postcss": "^8.4.31" } }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-8.0.1.tgz", + "integrity": "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -11726,11 +13748,6 @@ "node": ">= 0.10" } }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, "node_modules/pupa": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", @@ -12172,9 +14189,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dependencies": { "regenerate": "^1.4.2" }, @@ -12196,14 +14213,14 @@ } }, "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dependencies": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -12236,25 +14253,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + }, "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, "node_modules/rehype-raw": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", @@ -12490,6 +14504,14 @@ "entities": "^2.0.0" } }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -12691,9 +14713,9 @@ } }, "node_modules/search-insights": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.1.tgz", - "integrity": "sha512-HHFjYH/0AqXacETlIbe9EYc3UNlQYGNNTY0fZ/sWl6SweX+GDxq9NB5+RVoPLgEFuOtCz7M9dhYxqDnhbbF0eQ==", + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", "peer": true }, "node_modules/section-matter": { @@ -12816,24 +14838,23 @@ } }, "node_modules/serve-handler": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", - "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", "mime-types": "2.1.18", "minimatch": "3.1.2", "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", + "path-to-regexp": "3.3.0", "range-parser": "1.2.0" } }, "node_modules/serve-handler/node_modules/path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==" }, "node_modules/serve-index": { "version": "1.9.1", @@ -13203,9 +15224,9 @@ } }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==" }, "node_modules/string_decoder": { "version": "1.3.0", @@ -13557,14 +15578,6 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -13684,9 +15697,9 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "engines": { "node": ">=4" } @@ -13712,9 +15725,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "engines": { "node": ">=4" } @@ -13864,9 +15877,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "funding": [ { "type": "opencollective", @@ -13882,8 +15895,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -14185,17 +16198,17 @@ } }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dependencies": { - "@types/estree": "^1.0.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", @@ -14475,22 +16488,72 @@ } }, "node_modules/webpackbar": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", - "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", + "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.3", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "consola": "^3.2.3", + "figures": "^3.2.0", + "markdown-table": "^2.0.0", "pretty-time": "^1.1.0", - "std-env": "^3.0.1" + "std-env": "^3.7.0", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.21.3" }, "peerDependencies": { "webpack": "3 || 4 || 5" } }, + "node_modules/webpackbar/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/webpackbar/node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webpackbar/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpackbar/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", diff --git a/docs/package.json b/docs/package.json index f9853246c..2ff41b96b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,9 +14,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "3.5.2", - "@docusaurus/plugin-client-redirects": "^3.5.2", - "@docusaurus/preset-classic": "3.5.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/plugin-client-redirects": "^3.6.3", + "@docusaurus/preset-classic": "3.6.3", "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -25,8 +25,8 @@ "react-dom": "^18.0.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.5.2", - "@docusaurus/types": "3.5.2" + "@docusaurus/module-type-aliases": "3.6.3", + "@docusaurus/types": "3.6.3" }, "browserslist": { "production": [ From b389c4cee49909ec5dce63c7be007154ad973fc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:09:26 +0900 Subject: [PATCH 049/282] chore(deps): bump path-to-regexp from 1.8.0 to 1.9.0 in /docs (#1212) Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 1.8.0 to 1.9.0. - [Release notes](https://github.com/pillarjs/path-to-regexp/releases) - [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md) - [Commits](https://github.com/pillarjs/path-to-regexp/compare/v1.8.0...v1.9.0) --- updated-dependencies: - dependency-name: path-to-regexp dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 90ebb842f..fb60cb6bd 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -12151,9 +12151,9 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", + "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", "dependencies": { "isarray": "0.0.1" } From 072714f914da97d726e6c359d2771265926a9970 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:09:49 +0900 Subject: [PATCH 050/282] chore(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /docs (#1211) Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6. - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index fb60cb6bd..e2a708b9c 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -6115,9 +6115,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", From 15ad6ec5ceaa99d8da24b5d9775bab113148af65 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 5 Dec 2024 13:07:47 +0900 Subject: [PATCH 051/282] Update requirements.txt Upgrade the required slack-sdk version to the latest --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bdf4a1191..f0b0cf987 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -slack_sdk>=3.33.1,<4 +slack_sdk>=3.33.5,<4 From c4a802e430c317dacb16b556070e2d14f4aa7228 Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Thu, 5 Dec 2024 17:51:00 -0500 Subject: [PATCH 052/282] Expose loop param on asyncio based AsyncSocketModeHandler (#1216) Co-authored-by: Kazuhiro Sera --- slack_bolt/adapter/socket_mode/aiohttp/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/slack_bolt/adapter/socket_mode/aiohttp/__init__.py b/slack_bolt/adapter/socket_mode/aiohttp/__init__.py index 3c6c8610d..124daaa4a 100644 --- a/slack_bolt/adapter/socket_mode/aiohttp/__init__.py +++ b/slack_bolt/adapter/socket_mode/aiohttp/__init__.py @@ -4,6 +4,7 @@ from logging import Logger from time import time from typing import Optional +from asyncio import AbstractEventLoop from slack_sdk.socket_mode.aiohttp import SocketModeClient from slack_sdk.socket_mode.request import SocketModeRequest @@ -74,6 +75,7 @@ def __init__( web_client: Optional[AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10, + loop: Optional[AbstractEventLoop] = None, ): self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] @@ -83,6 +85,7 @@ def __init__( web_client=web_client if web_client is not None else app.client, proxy=proxy, ping_interval=ping_interval, + loop=loop, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] From d0a701cc74a353b9f54795d988ad51aaad61b0ca Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Fri, 6 Dec 2024 07:59:04 +0900 Subject: [PATCH 053/282] version 1.21.3 --- .../slack_bolt/adapter/aiohttp/index.html | 6 +- .../adapter/asgi/aiohttp/index.html | 8 +- .../adapter/asgi/async_handler.html | 8 +- .../slack_bolt/adapter/asgi/base_handler.html | 14 +- .../adapter/asgi/builtin/index.html | 23 +-- .../slack_bolt/adapter/asgi/http_request.html | 10 +- .../adapter/asgi/http_response.html | 10 +- .../slack_bolt/adapter/asgi/index.html | 23 +-- .../slack_bolt/adapter/asgi/utils.html | 6 +- .../adapter/aws_lambda/chalice_handler.html | 8 +- .../chalice_lazy_listener_runner.html | 18 +- .../adapter/aws_lambda/handler.html | 6 +- .../slack_bolt/adapter/aws_lambda/index.html | 6 +- .../adapter/aws_lambda/internals.html | 6 +- .../aws_lambda/lambda_s3_oauth_flow.html | 48 +---- .../aws_lambda/lazy_listener_runner.html | 18 +- .../aws_lambda/local_lambda_client.html | 6 +- .../slack_bolt/adapter/bottle/handler.html | 8 +- .../slack_bolt/adapter/bottle/index.html | 6 +- .../slack_bolt/adapter/cherrypy/handler.html | 6 +- .../slack_bolt/adapter/cherrypy/index.html | 6 +- .../slack_bolt/adapter/django/handler.html | 16 +- .../slack_bolt/adapter/django/index.html | 6 +- .../adapter/falcon/async_resource.html | 9 +- .../slack_bolt/adapter/falcon/index.html | 12 +- .../slack_bolt/adapter/falcon/resource.html | 12 +- .../adapter/fastapi/async_handler.html | 8 +- .../slack_bolt/adapter/fastapi/index.html | 8 +- .../slack_bolt/adapter/flask/handler.html | 6 +- .../slack_bolt/adapter/flask/index.html | 6 +- .../google_cloud_functions/handler.html | 16 +- .../adapter/google_cloud_functions/index.html | 6 +- .../api-docs/slack_bolt/adapter/index.html | 6 +- .../slack_bolt/adapter/pyramid/handler.html | 6 +- .../slack_bolt/adapter/pyramid/index.html | 6 +- .../adapter/sanic/async_handler.html | 10 +- .../slack_bolt/adapter/sanic/index.html | 8 +- .../adapter/socket_mode/aiohttp/index.html | 12 +- .../socket_mode/async_base_handler.html | 10 +- .../adapter/socket_mode/async_handler.html | 10 +- .../adapter/socket_mode/async_internals.html | 10 +- .../adapter/socket_mode/base_handler.html | 8 +- .../adapter/socket_mode/builtin/index.html | 8 +- .../slack_bolt/adapter/socket_mode/index.html | 8 +- .../adapter/socket_mode/internals.html | 10 +- .../socket_mode/websocket_client/index.html | 8 +- .../adapter/socket_mode/websockets/index.html | 10 +- .../adapter/starlette/async_handler.html | 10 +- .../slack_bolt/adapter/starlette/handler.html | 10 +- .../slack_bolt/adapter/starlette/index.html | 8 +- .../adapter/tornado/async_handler.html | 14 +- .../slack_bolt/adapter/tornado/handler.html | 14 +- .../slack_bolt/adapter/tornado/index.html | 14 +- .../slack_bolt/adapter/wsgi/handler.html | 14 +- .../slack_bolt/adapter/wsgi/http_request.html | 8 +- .../adapter/wsgi/http_response.html | 6 +- .../slack_bolt/adapter/wsgi/index.html | 14 +- .../slack_bolt/adapter/wsgi/internals.html | 6 +- docs/static/api-docs/slack_bolt/app/app.html | 68 +++---- .../api-docs/slack_bolt/app/async_app.html | 68 +++---- .../api-docs/slack_bolt/app/async_server.html | 10 +- .../static/api-docs/slack_bolt/app/index.html | 66 +++---- .../static/api-docs/slack_bolt/async_app.html | 149 +++++++------- .../authorization/async_authorize.html | 16 +- .../authorization/async_authorize_args.html | 14 +- .../slack_bolt/authorization/authorize.html | 12 +- .../authorization/authorize_args.html | 14 +- .../authorization/authorize_result.html | 34 ++-- .../slack_bolt/authorization/index.html | 34 ++-- .../api-docs/slack_bolt/context/ack/ack.html | 8 +- .../slack_bolt/context/ack/async_ack.html | 8 +- .../slack_bolt/context/ack/index.html | 8 +- .../slack_bolt/context/ack/internals.html | 6 +- .../assistant/assistant_utilities.html | 8 +- .../assistant/async_assistant_utilities.html | 8 +- .../slack_bolt/context/assistant/index.html | 6 +- .../context/assistant/internals.html | 6 +- .../assistant/thread_context/index.html | 10 +- .../thread_context_store/async_store.html | 8 +- .../default_async_store.html | 8 +- .../thread_context_store/default_store.html | 8 +- .../thread_context_store/file/index.html | 8 +- .../assistant/thread_context_store/index.html | 6 +- .../assistant/thread_context_store/store.html | 8 +- .../slack_bolt/context/async_context.html | 18 +- .../slack_bolt/context/base_context.html | 48 ++--- .../context/complete/async_complete.html | 10 +- .../slack_bolt/context/complete/complete.html | 10 +- .../slack_bolt/context/complete/index.html | 10 +- .../api-docs/slack_bolt/context/context.html | 18 +- .../slack_bolt/context/fail/async_fail.html | 10 +- .../slack_bolt/context/fail/fail.html | 10 +- .../slack_bolt/context/fail/index.html | 10 +- .../async_get_thread_context.html | 8 +- .../get_thread_context.html | 8 +- .../context/get_thread_context/index.html | 8 +- .../api-docs/slack_bolt/context/index.html | 18 +- .../context/respond/async_respond.html | 14 +- .../slack_bolt/context/respond/index.html | 14 +- .../slack_bolt/context/respond/internals.html | 6 +- .../slack_bolt/context/respond/respond.html | 14 +- .../async_save_thread_context.html | 8 +- .../context/save_thread_context/index.html | 8 +- .../save_thread_context.html | 8 +- .../slack_bolt/context/say/async_say.html | 16 +- .../slack_bolt/context/say/index.html | 18 +- .../slack_bolt/context/say/internals.html | 6 +- .../api-docs/slack_bolt/context/say/say.html | 18 +- .../context/set_status/async_set_status.html | 8 +- .../slack_bolt/context/set_status/index.html | 6 +- .../context/set_status/set_status.html | 6 +- .../async_set_suggested_prompts.html | 15 +- .../context/set_suggested_prompts/index.html | 13 +- .../set_suggested_prompts.html | 13 +- .../context/set_title/async_set_title.html | 8 +- .../slack_bolt/context/set_title/index.html | 6 +- .../context/set_title/set_title.html | 6 +- .../api-docs/slack_bolt/error/index.html | 12 +- docs/static/api-docs/slack_bolt/index.html | 185 +++++++++--------- .../slack_bolt/kwargs_injection/args.html | 32 +-- .../kwargs_injection/async_args.html | 32 +-- .../kwargs_injection/async_utils.html | 8 +- .../slack_bolt/kwargs_injection/index.html | 34 ++-- .../slack_bolt/kwargs_injection/utils.html | 8 +- .../lazy_listener/async_internals.html | 8 +- .../lazy_listener/async_runner.html | 10 +- .../lazy_listener/asyncio_runner.html | 6 +- .../slack_bolt/lazy_listener/index.html | 10 +- .../slack_bolt/lazy_listener/internals.html | 8 +- .../slack_bolt/lazy_listener/runner.html | 10 +- .../lazy_listener/thread_runner.html | 6 +- .../slack_bolt/listener/async_builtins.html | 10 +- .../slack_bolt/listener/async_listener.html | 22 +-- .../async_listener_completion_handler.html | 8 +- .../async_listener_error_handler.html | 10 +- .../async_listener_start_handler.html | 8 +- .../slack_bolt/listener/asyncio_runner.html | 10 +- .../slack_bolt/listener/builtins.html | 10 +- .../slack_bolt/listener/custom_listener.html | 10 +- .../api-docs/slack_bolt/listener/index.html | 16 +- .../slack_bolt/listener/listener.html | 12 +- .../listener/listener_completion_handler.html | 8 +- .../listener/listener_error_handler.html | 10 +- .../listener/listener_start_handler.html | 8 +- .../slack_bolt/listener/thread_runner.html | 10 +- .../listener_matcher/async_builtins.html | 8 +- .../async_listener_matcher.html | 14 +- .../slack_bolt/listener_matcher/builtins.html | 52 ++--- .../custom_listener_matcher.html | 8 +- .../slack_bolt/listener_matcher/index.html | 10 +- .../listener_matcher/listener_matcher.html | 8 +- .../api-docs/slack_bolt/logger/index.html | 10 +- .../api-docs/slack_bolt/logger/messages.html | 10 +- .../middleware/assistant/assistant.html | 24 +-- .../middleware/assistant/async_assistant.html | 24 +-- .../middleware/assistant/index.html | 24 +-- .../slack_bolt/middleware/async_builtins.html | 31 +-- .../middleware/async_custom_middleware.html | 8 +- .../middleware/async_middleware.html | 8 +- .../async_middleware_error_handler.html | 10 +- .../async_attaching_function_token.html | 6 +- .../attaching_function_token.html | 6 +- .../attaching_function_token/index.html | 6 +- .../authorization/async_authorization.html | 6 +- .../authorization/async_internals.html | 6 +- .../async_multi_teams_authorization.html | 8 +- .../async_single_team_authorization.html | 8 +- .../authorization/authorization.html | 6 +- .../middleware/authorization/index.html | 10 +- .../middleware/authorization/internals.html | 6 +- .../multi_teams_authorization.html | 8 +- .../single_team_authorization.html | 8 +- .../middleware/custom_middleware.html | 8 +- .../async_ignoring_self_events.html | 8 +- .../ignoring_self_events.html | 8 +- .../ignoring_self_events/index.html | 8 +- .../api-docs/slack_bolt/middleware/index.html | 24 +-- .../async_message_listener_matches.html | 8 +- .../message_listener_matches/index.html | 8 +- .../message_listener_matches.html | 8 +- .../slack_bolt/middleware/middleware.html | 8 +- .../middleware/middleware_error_handler.html | 10 +- .../async_request_verification.html | 8 +- .../request_verification/index.html | 8 +- .../request_verification.html | 8 +- .../middleware/ssl_check/async_ssl_check.html | 23 +-- .../middleware/ssl_check/index.html | 10 +- .../middleware/ssl_check/ssl_check.html | 10 +- .../async_url_verification.html | 8 +- .../middleware/url_verification/index.html | 8 +- .../url_verification/url_verification.html | 8 +- .../oauth/async_callback_options.html | 14 +- .../slack_bolt/oauth/async_internals.html | 8 +- .../slack_bolt/oauth/async_oauth_flow.html | 28 +-- .../oauth/async_oauth_settings.html | 20 +- .../slack_bolt/oauth/callback_options.html | 14 +- .../api-docs/slack_bolt/oauth/index.html | 28 +-- .../api-docs/slack_bolt/oauth/internals.html | 10 +- .../api-docs/slack_bolt/oauth/oauth_flow.html | 28 +-- .../slack_bolt/oauth/oauth_settings.html | 20 +- .../slack_bolt/request/async_internals.html | 8 +- .../slack_bolt/request/async_request.html | 12 +- .../api-docs/slack_bolt/request/index.html | 12 +- .../slack_bolt/request/internals.html | 40 ++-- .../slack_bolt/request/payload_utils.html | 22 +-- .../api-docs/slack_bolt/request/request.html | 12 +- .../api-docs/slack_bolt/response/index.html | 8 +- .../slack_bolt/response/response.html | 8 +- .../api-docs/slack_bolt/util/async_utils.html | 8 +- .../api-docs/slack_bolt/util/index.html | 6 +- .../api-docs/slack_bolt/util/utils.html | 14 +- docs/static/api-docs/slack_bolt/version.html | 6 +- .../api-docs/slack_bolt/workflows/index.html | 6 +- .../slack_bolt/workflows/step/async_step.html | 30 +-- .../workflows/step/async_step_middleware.html | 6 +- .../slack_bolt/workflows/step/index.html | 14 +- .../slack_bolt/workflows/step/internals.html | 6 +- .../slack_bolt/workflows/step/step.html | 30 +-- .../workflows/step/step_middleware.html | 6 +- .../step/utilities/async_complete.html | 6 +- .../step/utilities/async_configure.html | 8 +- .../workflows/step/utilities/async_fail.html | 6 +- .../step/utilities/async_update.html | 6 +- .../workflows/step/utilities/complete.html | 6 +- .../workflows/step/utilities/configure.html | 6 +- .../workflows/step/utilities/fail.html | 6 +- .../workflows/step/utilities/index.html | 6 +- .../workflows/step/utilities/update.html | 6 +- slack_bolt/version.py | 2 +- 229 files changed, 1510 insertions(+), 1614 deletions(-) diff --git a/docs/static/api-docs/slack_bolt/adapter/aiohttp/index.html b/docs/static/api-docs/slack_bolt/adapter/aiohttp/index.html index 54bee5e0f..0b2b6e848 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aiohttp/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/aiohttp/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aiohttp API documentation - + @@ -70,7 +70,7 @@

                      Functions

                      diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html index daecab747..b36d86ed9 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi.aiohttp API documentation - + @@ -37,7 +37,7 @@

                      Classes

                      class AsyncSlackRequestHandler -(app: AsyncApp, path: str = '/slack/events') +(app: AsyncApp,
                      path: str = '/slack/events')

                      Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. @@ -155,7 +155,7 @@

                      diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html index b48da50e8..9bf506f09 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi.async_handler API documentation - + @@ -37,7 +37,7 @@

                      Classes

                      class AsyncSlackRequestHandler -(app: AsyncApp, path: str = '/slack/events') +(app: AsyncApp,
                      path: str = '/slack/events')

                      Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. @@ -155,7 +155,7 @@

                      -

                      Generated by pdoc 0.11.1.

                      +

                      Generated by pdoc 0.11.3.

                      diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html b/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html index 72017bb48..37deef2d1 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi.base_handler API documentation - + @@ -107,7 +107,7 @@

                      Subclasses

                    Class variables

                    -
                    var app : Union[App, AsyncApp]
                    +
                    var appApp | AsyncApp
                    @@ -119,19 +119,19 @@

                    Class variables

                    Methods

                    -async def dispatch(self, request: AsgiHttpRequest) ‑> BoltResponse +async def dispatch(self,
                    request: AsgiHttpRequest) ‑> BoltResponse

                    Dispatches a request to the Bolt App

                    -async def handle_callback(self, request: AsgiHttpRequest) ‑> BoltResponse +async def handle_callback(self,
                    request: AsgiHttpRequest) ‑> BoltResponse

                    Handles the callback of the OAuthFlow

                    -async def handle_installation(self, request: AsgiHttpRequest) ‑> BoltResponse +async def handle_installation(self,
                    request: AsgiHttpRequest) ‑> BoltResponse

                    Handles installation of the OAuthFlow

                    @@ -169,7 +169,7 @@

                    -

                    Generated by pdoc 0.11.1.

                    +

                    Generated by pdoc 0.11.3.

                    diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html index 7a21b8bce..07f132169 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi.builtin API documentation - + @@ -37,7 +37,7 @@

                    Classes

                    class SlackRequestHandler -(app: App, path: str = '/slack/events') +(app: App,
                    path: str = '/slack/events')

                    Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. @@ -111,17 +111,6 @@

                    Subclasses

                    -

                    Class variables

                    -
                    -
                    var app : Union[App, AsyncApp]
                    -
                    -
                    -
                    -
                    var path : str
                    -
                    -
                    -
                    -

                    Inherited members

                    • BaseSlackRequestHandler: @@ -150,10 +139,6 @@

                      Inherited members

                    • @@ -161,7 +146,7 @@

                      -

                      Generated by pdoc 0.11.1.

                      +

                      Generated by pdoc 0.11.3.

                      diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html b/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html index cc9a50c8e..e1f8a58d1 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi.http_request API documentation - + @@ -37,7 +37,7 @@

                      Classes

                      class AsgiHttpRequest -(scope: Dict[str, Union[str, bytes, Iterable[Tuple[bytes, bytes]]]], receive: Callable) +(scope: Dict[str, str | bytes | Iterable[Tuple[bytes, bytes]]],
                      receive: Callable)
                      @@ -87,7 +87,7 @@

                      Instance variables

                      Methods

                      -def get_headers(self) ‑> Dict[str, Union[str, Sequence[str]]] +def get_headers(self) ‑> Dict[str, str | Sequence[str]]
                      @@ -131,7 +131,7 @@

                      -

                      Generated by pdoc 0.11.1.

                      +

                      Generated by pdoc 0.11.3.

                      diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/http_response.html b/docs/static/api-docs/slack_bolt/adapter/asgi/http_response.html index d8a0e4ad6..fa51ddb6c 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/http_response.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/http_response.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi.http_response API documentation - + @@ -88,13 +88,13 @@

                      Instance variables

                      Methods

                      -def get_response_body(self) ‑> Dict[str, Union[str, bytes, bool]] +def get_response_body(self) ‑> Dict[str, str | bytes | bool]
                      -def get_response_start(self) ‑> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]] +def get_response_start(self) ‑> Dict[str, str | int | Iterable[Tuple[bytes, bytes]]]
                      @@ -132,7 +132,7 @@

                      diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/index.html index 9c05b7cb2..3e6dc537b 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi API documentation - + @@ -68,7 +68,7 @@

                      Classes

                      class SlackRequestHandler -(app: App, path: str = '/slack/events') +(app: App,
                      path: str = '/slack/events')

                      Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. @@ -142,17 +142,6 @@

                      Subclasses

                      -

                      Class variables

                      -
                      -
                      var app : Union[App, AsyncApp]
                      -
                      -
                      -
                      -
                      var path : str
                      -
                      -
                      -
                      -

                      Inherited members

                      • BaseSlackRequestHandler: @@ -192,10 +181,6 @@

                        Inherited members

                      • @@ -203,7 +188,7 @@

                        -

                        Generated by pdoc 0.11.1.

                        +

                        Generated by pdoc 0.11.3.

                        diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/utils.html b/docs/static/api-docs/slack_bolt/adapter/asgi/utils.html index 5a5f116f1..ce55c6dc3 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/utils.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/utils.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.asgi.utils API documentation - + @@ -49,7 +49,7 @@

                        Module slack_bolt.adapter.asgi.utils

                        diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html index 21ad6058d..b5cbb0a1e 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda.chalice_handler API documentation - + @@ -58,7 +58,7 @@

                        Classes

                        class ChaliceSlackRequestHandler -(app: App, chalice: chalice.app.Chalice, lambda_client: Optional[botocore.client.BaseClient] = None) +(app: App,
                        chalice: chalice.app.Chalice,
                        lambda_client: botocore.client.BaseClient | None = None)
                        @@ -191,7 +191,7 @@

                        -

                        Generated by pdoc 0.11.1.

                        +

                        Generated by pdoc 0.11.3.

                        diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html index d9f6d2ad4..4a882d5bd 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda.chalice_lazy_listener_runner API documentation - + @@ -37,7 +37,7 @@

                        Classes

                        class ChaliceLazyListenerRunner -(logger: logging.Logger, lambda_client: Optional[botocore.client.BaseClient] = None) +(logger: logging.Logger,
                        lambda_client: botocore.client.BaseClient | None = None)
                        @@ -78,13 +78,6 @@

                        Ancestors

                        -

                        Class variables

                        -
                        -
                        var logger : logging.Logger
                        -
                        -
                        -
                        -

                        Inherited members

                        • LazyListenerRunner: @@ -112,9 +105,6 @@

                          Inherited members

                        • @@ -122,7 +112,7 @@

                          -

                          Generated by pdoc 0.11.1.

                          +

                          Generated by pdoc 0.11.3.

                          diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html index f836f583c..65923f86f 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda.handler API documentation - + @@ -178,7 +178,7 @@

                          diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html index 9b1e30b2a..7b3f68825 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda API documentation - + @@ -192,7 +192,7 @@

                          -

                          Generated by pdoc 0.11.1.

                          +

                          Generated by pdoc 0.11.3.

                          diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/internals.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/internals.html index 1ab7320f0..28b2ed6d8 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/internals.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda.internals API documentation - + @@ -49,7 +49,7 @@

                          Module slack_bolt.adapter.aws_lambda.internals diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html index cbf880d2b..7a8691e75 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow API documentation - + @@ -37,7 +37,7 @@

                          Classes

                          class LambdaS3OAuthFlow -(*, client: Optional[slack_sdk.web.client.WebClient] = None, logger: Optional[logging.Logger] = None, settings: Optional[OAuthSettings] = None, oauth_state_bucket_name: Optional[str] = None, installation_bucket_name: Optional[str] = None) +(*,
                          client: slack_sdk.web.client.WebClient | None = None,
                          logger: logging.Logger | None = None,
                          settings: OAuthSettings | None = None,
                          oauth_state_bucket_name: str | None = None,
                          installation_bucket_name: str | None = None)

                          The module to run the Slack app installation flow (OAuth flow).

                          @@ -119,37 +119,6 @@

                          Ancestors

                          -

                          Class variables

                          -
                          -
                          var client_id : str
                          -
                          -
                          -
                          -
                          var failure_handler : Callable[[FailureArgs], BoltResponse]
                          -
                          -
                          -
                          -
                          var install_path : str
                          -
                          -
                          -
                          -
                          var redirect_uri : Optional[str]
                          -
                          -
                          -
                          -
                          var redirect_uri_path : str
                          -
                          -
                          -
                          -
                          var settingsOAuthSettings
                          -
                          -
                          -
                          -
                          var success_handler : Callable[[SuccessArgs], BoltResponse]
                          -
                          -
                          -
                          -

                          Instance variables

                          prop client : slack_sdk.web.client.WebClient
                          @@ -199,16 +168,9 @@

                          Instance variables

                          • LambdaS3OAuthFlow

                            - @@ -217,7 +179,7 @@

                            -

                            Generated by pdoc 0.11.1.

                            +

                            Generated by pdoc 0.11.3.

                            diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html index c83e0c628..880cfa93b 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda.lazy_listener_runner API documentation - + @@ -37,7 +37,7 @@

                            Classes

                            class LambdaLazyListenerRunner -(logger: logging.Logger, lambda_client: Optional[Any] = None) +(logger: logging.Logger, lambda_client: Any | None = None)
                            @@ -70,13 +70,6 @@

                            Ancestors

                            -

                            Class variables

                            -
                            -
                            var logger : logging.Logger
                            -
                            -
                            -
                            -

                            Inherited members

                            • LazyListenerRunner: @@ -104,9 +97,6 @@

                              Inherited members

                            • @@ -114,7 +104,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/local_lambda_client.html b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/local_lambda_client.html index 331f7e593..75fd507b2 100644 --- a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/local_lambda_client.html +++ b/docs/static/api-docs/slack_bolt/adapter/aws_lambda/local_lambda_client.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.aws_lambda.local_lambda_client API documentation - + @@ -106,7 +106,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html b/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html index a8615c7f2..47253e59e 100644 --- a/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.bottle.handler API documentation - + @@ -34,7 +34,7 @@

                              Module slack_bolt.adapter.bottle.handler

                              Functions

                              -def set_response(bolt_resp: BoltResponse, resp: bottle.BaseResponse) ‑> None +def set_response(bolt_resp: BoltResponse,
                              resp: bottle.BaseResponse) ‑> None
                              @@ -127,7 +127,7 @@

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/bottle/index.html b/docs/static/api-docs/slack_bolt/adapter/bottle/index.html index 941353ccd..11389133d 100644 --- a/docs/static/api-docs/slack_bolt/adapter/bottle/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/bottle/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.bottle API documentation - + @@ -118,7 +118,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html b/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html index 9aa328d64..96829170e 100644 --- a/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.cherrypy.handler API documentation - + @@ -136,7 +136,7 @@

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html b/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html index 142190a72..a0a15e410 100644 --- a/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.cherrypy API documentation - + @@ -120,7 +120,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/django/handler.html b/docs/static/api-docs/slack_bolt/adapter/django/handler.html index 52f3c4909..62140c7ee 100644 --- a/docs/static/api-docs/slack_bolt/adapter/django/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/django/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.django.handler API documentation - + @@ -154,13 +154,6 @@

                              Ancestors

                            • ThreadLazyListenerRunner
                            • LazyListenerRunner
                            -

                            Class variables

                            -
                            -
                            var logger : logging.Logger
                            -
                            -
                            -
                            -

                            Inherited members

                            • ThreadLazyListenerRunner: @@ -290,9 +283,6 @@

                              DjangoThreadLazyListenerRunner

                              -
                            • SlackRequestHandler

                              @@ -306,7 +296,7 @@

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/django/index.html b/docs/static/api-docs/slack_bolt/adapter/django/index.html index 6fcb62b8d..a4eaf4a55 100644 --- a/docs/static/api-docs/slack_bolt/adapter/django/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/django/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.django API documentation - + @@ -163,7 +163,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html b/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html index 050729721..29b780634 100644 --- a/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html +++ b/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.falcon.async_resource API documentation - + @@ -81,7 +81,8 @@

                              Classes

                              return resp.status = "404" - resp.body = "The page is not found..." + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = "The page is not found..." # type: ignore[assignment] async def on_post(self, req: Request, resp: Response): bolt_req = await self._to_bolt_request(req) @@ -159,7 +160,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/index.html b/docs/static/api-docs/slack_bolt/adapter/falcon/index.html index b54b3cd22..a688c6661 100644 --- a/docs/static/api-docs/slack_bolt/adapter/falcon/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/falcon/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.falcon API documentation - + @@ -86,7 +86,8 @@

                              Classes

                              return resp.status = "404" - resp.body = "The page is not found..." + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = "The page is not found..." # type: ignore[assignment] def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -102,7 +103,8 @@

                              Classes

                              def _write_response(self, bolt_resp: BoltResponse, resp: Response): if falcon_version.__version__.startswith("2."): - resp.body = bolt_resp.body + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = bolt_resp.body # type: ignore[assignment] else: resp.text = bolt_resp.body @@ -174,7 +176,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html b/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html index a00b3be11..b3bdf6cff 100644 --- a/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html +++ b/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.falcon.resource API documentation - + @@ -75,7 +75,8 @@

                              Classes

                              return resp.status = "404" - resp.body = "The page is not found..." + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = "The page is not found..." # type: ignore[assignment] def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -91,7 +92,8 @@

                              Classes

                              def _write_response(self, bolt_resp: BoltResponse, resp: Response): if falcon_version.__version__.startswith("2."): - resp.body = bolt_resp.body + # Falcon 4.x w/ mypy fails to correctly infer the str type here + resp.body = bolt_resp.body # type: ignore[assignment] else: resp.text = bolt_resp.body @@ -157,7 +159,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html index 409efd900..9fec3596a 100644 --- a/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.fastapi.async_handler API documentation - + @@ -76,7 +76,7 @@

                              Classes

                              Methods

                              -async def handle(self, req: starlette.requests.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> starlette.responses.Response +async def handle(self,
                              req: starlette.requests.Request,
                              addition_context_properties: Dict[str, Any] | None = None) ‑> starlette.responses.Response
                              @@ -110,7 +110,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html b/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html index 996afbf54..b0dcb6a2f 100644 --- a/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.fastapi API documentation - + @@ -79,7 +79,7 @@

                              Classes

                              Methods

                              -async def handle(self, req: starlette.requests.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> starlette.responses.Response +async def handle(self,
                              req: starlette.requests.Request,
                              addition_context_properties: Dict[str, Any] | None = None) ‑> starlette.responses.Response
                              @@ -118,7 +118,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/flask/handler.html b/docs/static/api-docs/slack_bolt/adapter/flask/handler.html index 599ed63be..249df9c95 100644 --- a/docs/static/api-docs/slack_bolt/adapter/flask/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/flask/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.flask.handler API documentation - + @@ -123,7 +123,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/flask/index.html b/docs/static/api-docs/slack_bolt/adapter/flask/index.html index 15bfa55f2..782743bcd 100644 --- a/docs/static/api-docs/slack_bolt/adapter/flask/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/flask/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.flask API documentation - + @@ -114,7 +114,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html index 8e4df3885..2a45c1a3f 100644 --- a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.google_cloud_functions.handler API documentation - + @@ -56,13 +56,6 @@

                              Ancestors

                              -

                              Class variables

                              -
                              -
                              var logger : logging.Logger
                              -
                              -
                              -
                              -

                              Inherited members

                              • LazyListenerRunner: @@ -133,9 +126,6 @@

                                Methods

                                • NoopLazyListenerRunner

                                  -
                                • SlackRequestHandler

                                  @@ -149,7 +139,7 @@

                                  -

                                  Generated by pdoc 0.11.1.

                                  +

                                  Generated by pdoc 0.11.3.

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html index 7d5e9ee63..8a905fb54 100644 --- a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.google_cloud_functions API documentation - + @@ -117,7 +117,7 @@

                                  -

                                  Generated by pdoc 0.11.1.

                                  +

                                  Generated by pdoc 0.11.3.

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/index.html b/docs/static/api-docs/slack_bolt/adapter/index.html index 62719beab..75b5c9d55 100644 --- a/docs/static/api-docs/slack_bolt/adapter/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter API documentation - + @@ -137,7 +137,7 @@

                                  Sub-modules

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html b/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html index fa64e6931..cc45e56c3 100644 --- a/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.pyramid.handler API documentation - + @@ -126,7 +126,7 @@

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html b/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html index d962e458b..ce6ba815a 100644 --- a/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.pyramid API documentation - + @@ -117,7 +117,7 @@

                                  -

                                  Generated by pdoc 0.11.1.

                                  +

                                  Generated by pdoc 0.11.3.

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html index a94007bcc..97b04ce1e 100644 --- a/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.sanic.async_handler API documentation - + @@ -34,7 +34,7 @@

                                  Module slack_bolt.adapter.sanic.async_handler

                                  Functions

                                  -def to_async_bolt_request(req: sanic.request.types.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> AsyncBoltRequest +def to_async_bolt_request(req: sanic.request.types.Request,
                                  addition_context_properties: Dict[str, Any] | None = None) ‑> AsyncBoltRequest
                                  @@ -87,7 +87,7 @@

                                  Classes

                                  Methods

                                  -async def handle(self, req: sanic.request.types.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> sanic.response.types.HTTPResponse +async def handle(self,
                                  req: sanic.request.types.Request,
                                  addition_context_properties: Dict[str, Any] | None = None) ‑> sanic.response.types.HTTPResponse
                                  @@ -127,7 +127,7 @@

                                  -

                                  Generated by pdoc 0.11.1.

                                  +

                                  Generated by pdoc 0.11.3.

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/sanic/index.html b/docs/static/api-docs/slack_bolt/adapter/sanic/index.html index be50d1ab4..6885f9896 100644 --- a/docs/static/api-docs/slack_bolt/adapter/sanic/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/sanic/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.sanic API documentation - + @@ -79,7 +79,7 @@

                                  Classes

                                  Methods

                                  -async def handle(self, req: sanic.request.types.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> sanic.response.types.HTTPResponse +async def handle(self,
                                  req: sanic.request.types.Request,
                                  addition_context_properties: Dict[str, Any] | None = None) ‑> sanic.response.types.HTTPResponse
                                  @@ -118,7 +118,7 @@

                                  -

                                  Generated by pdoc 0.11.1.

                                  +

                                  Generated by pdoc 0.11.3.

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html index 9f2212fd3..be06a800c 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode.aiohttp API documentation - + @@ -38,7 +38,7 @@

                                  Classes

                                  class AsyncSocketModeHandler -(app: AsyncApp, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10) +(app: AsyncApp,
                                  app_token: str | None = None,
                                  logger: logging.Logger | None = None,
                                  web_client: slack_sdk.web.async_client.AsyncWebClient | None = None,
                                  proxy: str | None = None,
                                  ping_interval: float = 10,
                                  loop: asyncio.events.AbstractEventLoop | None = None)
                                  @@ -59,6 +59,7 @@

                                  Classes

                                  web_client: Optional[AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10, + loop: Optional[AbstractEventLoop] = None, ): self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] @@ -68,6 +69,7 @@

                                  Classes

                                  web_client=web_client if web_client is not None else app.client, proxy=proxy, ping_interval=ping_interval, + loop=loop, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] @@ -110,7 +112,7 @@

                                  Inherited members

                                  class SocketModeHandler -(app: App, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10) +(app: App,
                                  app_token: str | None = None,
                                  logger: logging.Logger | None = None,
                                  web_client: slack_sdk.web.async_client.AsyncWebClient | None = None,
                                  proxy: str | None = None,
                                  ping_interval: float = 10)

                                  Socket Mode adapter for Bolt apps

                                  @@ -242,7 +244,7 @@

                                  diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html index b9a211803..d1b60ac6c 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode.async_base_handler API documentation - + @@ -91,7 +91,7 @@

                                  Subclasses

                                Class variables

                                -
                                var app : Union[AppAsyncApp]
                                +
                                var appApp | AsyncApp
                                @@ -121,7 +121,7 @@

                                Methods

                                Disconnects the current WebSocket connection with the Socket Mode server

                              -async def handle(self, client: slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient, req: slack_sdk.socket_mode.request.SocketModeRequest) ‑> None +async def handle(self,
                              client: slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient,
                              req: slack_sdk.socket_mode.request.SocketModeRequest) ‑> None

                              Handles Socket Mode envelope requests through a WebSocket connection.

                              @@ -176,7 +176,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html index 7234d62f9..8dedfa3b4 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode.async_handler API documentation - + @@ -38,7 +38,7 @@

                              Classes

                              class AsyncSocketModeHandler -(app: AsyncApp, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10) +(app: AsyncApp,
                              app_token: str | None = None,
                              logger: logging.Logger | None = None,
                              web_client: slack_sdk.web.async_client.AsyncWebClient | None = None,
                              proxy: str | None = None,
                              ping_interval: float = 10,
                              loop: asyncio.events.AbstractEventLoop | None = None)
                              @@ -59,6 +59,7 @@

                              Classes

                              web_client: Optional[AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10, + loop: Optional[AbstractEventLoop] = None, ): self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] @@ -68,6 +69,7 @@

                              Classes

                              web_client=web_client if web_client is not None else app.client, proxy=proxy, ping_interval=ping_interval, + loop=loop, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] @@ -137,7 +139,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_internals.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_internals.html index 21fabc19a..a8cab1e0b 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_internals.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_internals.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode.async_internals API documentation - + @@ -35,13 +35,13 @@

                              Module slack_bolt.adapter.socket_mode.async_internalsFunctions

                              -async def run_async_bolt_app(app: AsyncApp, req: slack_sdk.socket_mode.request.SocketModeRequest) +async def run_async_bolt_app(app: AsyncApp,
                              req: slack_sdk.socket_mode.request.SocketModeRequest)
                              -async def send_async_response(client: slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient, req: slack_sdk.socket_mode.request.SocketModeRequest, bolt_resp: BoltResponse, start_time: float) +async def send_async_response(client: slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient,
                              req: slack_sdk.socket_mode.request.SocketModeRequest,
                              bolt_resp: BoltResponse,
                              start_time: float)
                              @@ -71,7 +71,7 @@

                              Functions

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html index f8d221d9f..0fc07f7ed 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html @@ -3,14 +3,14 @@ - + slack_bolt.adapter.socket_mode.base_handler API documentation - + @@ -127,7 +127,7 @@

                              Methods

                              Disconnects the current WebSocket connection with the Socket Mode server

                              -def handle(self, client: slack_sdk.socket_mode.client.BaseSocketModeClient, req: slack_sdk.socket_mode.request.SocketModeRequest) ‑> None +def handle(self,
                              client: slack_sdk.socket_mode.client.BaseSocketModeClient,
                              req: slack_sdk.socket_mode.request.SocketModeRequest) ‑> None

                              Handles Socket Mode envelope requests through a WebSocket connection.

                              @@ -182,7 +182,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html index 776dac0c1..5a6a6dda2 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode.builtin API documentation - + @@ -38,7 +38,7 @@

                              Classes

                              class SocketModeHandler -(app: App, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.client.WebClient] = None, proxy: Optional[str] = None, proxy_headers: Optional[Dict[str, str]] = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 10, receive_buffer_size: int = 1024, concurrency: int = 10) +(app: App,
                              app_token: str | None = None,
                              logger: logging.Logger | None = None,
                              web_client: slack_sdk.web.client.WebClient | None = None,
                              proxy: str | None = None,
                              proxy_headers: Dict[str, str] | None = None,
                              auto_reconnect_enabled: bool = True,
                              trace_enabled: bool = False,
                              all_message_trace_enabled: bool = False,
                              ping_pong_trace_enabled: bool = False,
                              ping_interval: float = 10,
                              receive_buffer_size: int = 1024,
                              concurrency: int = 10)

                              Socket Mode adapter for Bolt apps

                              @@ -197,7 +197,7 @@

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html index b45fa55fb..7fd56eb3a 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode API documentation - + @@ -85,7 +85,7 @@

                              Classes

                              class SocketModeHandler -(app: App, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.client.WebClient] = None, proxy: Optional[str] = None, proxy_headers: Optional[Dict[str, str]] = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 10, receive_buffer_size: int = 1024, concurrency: int = 10) +(app: App,
                              app_token: str | None = None,
                              logger: logging.Logger | None = None,
                              web_client: slack_sdk.web.client.WebClient | None = None,
                              proxy: str | None = None,
                              proxy_headers: Dict[str, str] | None = None,
                              auto_reconnect_enabled: bool = True,
                              trace_enabled: bool = False,
                              all_message_trace_enabled: bool = False,
                              ping_pong_trace_enabled: bool = False,
                              ping_interval: float = 10,
                              receive_buffer_size: int = 1024,
                              concurrency: int = 10)

                              Socket Mode adapter for Bolt apps

                              @@ -257,7 +257,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/internals.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/internals.html index 51ad3c413..2c40965a4 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/internals.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode.internals API documentation - + @@ -35,13 +35,13 @@

                              Module slack_bolt.adapter.socket_mode.internalsFunctions

                              -def run_bolt_app(app: App, req: slack_sdk.socket_mode.request.SocketModeRequest) +def run_bolt_app(app: App,
                              req: slack_sdk.socket_mode.request.SocketModeRequest)
                              -def send_response(client: slack_sdk.socket_mode.client.BaseSocketModeClient, req: slack_sdk.socket_mode.request.SocketModeRequest, bolt_resp: BoltResponse, start_time: float) +def send_response(client: slack_sdk.socket_mode.client.BaseSocketModeClient,
                              req: slack_sdk.socket_mode.request.SocketModeRequest,
                              bolt_resp: BoltResponse,
                              start_time: float)
                              @@ -71,7 +71,7 @@

                              Functions

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html index 074342c85..ff14fe4a8 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.socket_mode.websocket_client API documentation - + @@ -38,7 +38,7 @@

                              Classes

                              class SocketModeHandler -(app: App, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.client.WebClient] = None, ping_interval: float = 10, concurrency: int = 10, http_proxy_host: Optional[str] = None, http_proxy_port: Optional[int] = None, http_proxy_auth: Optional[Tuple[str, str]] = None, proxy_type: Optional[str] = None, trace_enabled: bool = False) +(app: App,
                              app_token: str | None = None,
                              logger: logging.Logger | None = None,
                              web_client: slack_sdk.web.client.WebClient | None = None,
                              ping_interval: float = 10,
                              concurrency: int = 10,
                              http_proxy_host: str | None = None,
                              http_proxy_port: int | None = None,
                              http_proxy_auth: Tuple[str, str] | None = None,
                              proxy_type: str | None = None,
                              trace_enabled: bool = False)

                              Socket Mode adapter for Bolt apps

                              @@ -187,7 +187,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html index 415070179..c00f83fde 100644 --- a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html @@ -3,14 +3,14 @@ - + slack_bolt.adapter.socket_mode.websockets API documentation - + @@ -40,7 +40,7 @@

                              Classes

                              class AsyncSocketModeHandler -(app: AsyncApp, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, ping_interval: float = 10) +(app: AsyncApp,
                              app_token: str | None = None,
                              logger: logging.Logger | None = None,
                              web_client: slack_sdk.web.async_client.AsyncWebClient | None = None,
                              ping_interval: float = 10)
                              @@ -110,7 +110,7 @@

                              Inherited members

                              class SocketModeHandler -(app: App, app_token: Optional[str] = None, logger: Optional[logging.Logger] = None, web_client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, ping_interval: float = 10) +(app: App,
                              app_token: str | None = None,
                              logger: logging.Logger | None = None,
                              web_client: slack_sdk.web.async_client.AsyncWebClient | None = None,
                              ping_interval: float = 10)

                              Socket Mode adapter for Bolt apps.

                              @@ -244,7 +244,7 @@

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html index d5a8ce076..07e19c9b3 100644 --- a/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.starlette.async_handler API documentation - + @@ -34,7 +34,7 @@

                              Module slack_bolt.adapter.starlette.async_handler

                              Functions

                              -def to_async_bolt_request(req: starlette.requests.Request, body: bytes, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> AsyncBoltRequest +def to_async_bolt_request(req: starlette.requests.Request,
                              body: bytes,
                              addition_context_properties: Dict[str, Any] | None = None) ‑> AsyncBoltRequest
                              @@ -91,7 +91,7 @@

                              Classes

                              Methods

                              -async def handle(self, req: starlette.requests.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> starlette.responses.Response +async def handle(self,
                              req: starlette.requests.Request,
                              addition_context_properties: Dict[str, Any] | None = None) ‑> starlette.responses.Response
                              @@ -131,7 +131,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html b/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html index b5297f46e..a49827582 100644 --- a/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.starlette.handler API documentation - + @@ -34,7 +34,7 @@

                              Module slack_bolt.adapter.starlette.handler

                              Functions

                              -def to_bolt_request(req: starlette.requests.Request, body: bytes, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> BoltRequest +def to_bolt_request(req: starlette.requests.Request,
                              body: bytes,
                              addition_context_properties: Dict[str, Any] | None = None) ‑> BoltRequest
                              @@ -87,7 +87,7 @@

                              Classes

                              Methods

                              -async def handle(self, req: starlette.requests.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> starlette.responses.Response +async def handle(self,
                              req: starlette.requests.Request,
                              addition_context_properties: Dict[str, Any] | None = None) ‑> starlette.responses.Response
                              @@ -127,7 +127,7 @@

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/index.html b/docs/static/api-docs/slack_bolt/adapter/starlette/index.html index 02b84c06c..fddd172fe 100644 --- a/docs/static/api-docs/slack_bolt/adapter/starlette/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/starlette/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.starlette API documentation - + @@ -83,7 +83,7 @@

                              Classes

                              Methods

                              -async def handle(self, req: starlette.requests.Request, addition_context_properties: Optional[Dict[str, Any]] = None) ‑> starlette.responses.Response +async def handle(self,
                              req: starlette.requests.Request,
                              addition_context_properties: Dict[str, Any] | None = None) ‑> starlette.responses.Response
                              @@ -123,7 +123,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html index 6926e3eb5..14de45e75 100644 --- a/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.tornado.async_handler API documentation - + @@ -46,7 +46,7 @@

                              Classes

                              class AsyncSlackEventsHandler -(application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any) +(application: Application,
                              request: tornado.httputil.HTTPServerRequest,
                              **kwargs: Any)

                              Base class for HTTP request handlers.

                              @@ -75,7 +75,7 @@

                              Ancestors

                              Methods

                              -def initialize(self, app: AsyncApp) +def initialize(self,
                              app: AsyncApp)
                              @@ -90,7 +90,7 @@

                              Methods

                              class AsyncSlackOAuthHandler -(application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any) +(application: Application,
                              request: tornado.httputil.HTTPServerRequest,
                              **kwargs: Any)

                              Base class for HTTP request handlers.

                              @@ -133,7 +133,7 @@

                              Methods

                              -def initialize(self, app: AsyncApp) +def initialize(self,
                              app: AsyncApp)
                              @@ -180,7 +180,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html b/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html index 29d364937..3d0fb7ecb 100644 --- a/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.tornado.handler API documentation - + @@ -52,7 +52,7 @@

                              Classes

                              class SlackEventsHandler -(application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any) +(application: Application,
                              request: tornado.httputil.HTTPServerRequest,
                              **kwargs: Any)

                              Base class for HTTP request handlers.

                              @@ -81,7 +81,7 @@

                              Ancestors

                              Methods

                              -def initialize(self, app: App) +def initialize(self,
                              app: App)
                              @@ -96,7 +96,7 @@

                              Methods

                              class SlackOAuthHandler -(application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any) +(application: Application,
                              request: tornado.httputil.HTTPServerRequest,
                              **kwargs: Any)

                              Base class for HTTP request handlers.

                              @@ -139,7 +139,7 @@

                              Methods

                              -def initialize(self, app: App) +def initialize(self,
                              app: App)
                              @@ -187,7 +187,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/index.html b/docs/static/api-docs/slack_bolt/adapter/tornado/index.html index 920fb845f..f73b7a5d3 100644 --- a/docs/static/api-docs/slack_bolt/adapter/tornado/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/tornado/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.tornado API documentation - + @@ -48,7 +48,7 @@

                              Classes

                              class SlackEventsHandler -(application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any) +(application: Application,
                              request: tornado.httputil.HTTPServerRequest,
                              **kwargs: Any)

                              Base class for HTTP request handlers.

                              @@ -77,7 +77,7 @@

                              Ancestors

                              Methods

                              -def initialize(self, app: App) +def initialize(self,
                              app: App)
                              @@ -92,7 +92,7 @@

                              Methods

                              class SlackOAuthHandler -(application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any) +(application: Application,
                              request: tornado.httputil.HTTPServerRequest,
                              **kwargs: Any)

                              Base class for HTTP request handlers.

                              @@ -135,7 +135,7 @@

                              Methods

                              -def initialize(self, app: App) +def initialize(self,
                              app: App)
                              @@ -183,7 +183,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html index ad7ba0ce8..f76c4665d 100644 --- a/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.wsgi.handler API documentation - + @@ -37,7 +37,7 @@

                              Classes

                              class SlackRequestHandler -(app: App, path: str = '/slack/events') +(app: App,
                              path: str = '/slack/events')

                              Setup Bolt as a WSGI web framework, this will make your application compatible with WSGI web servers. @@ -144,19 +144,19 @@

                              Args

                              Methods

                              -def dispatch(self, request: WsgiHttpRequest) ‑> BoltResponse +def dispatch(self,
                              request: WsgiHttpRequest) ‑> BoltResponse
                              -def handle_callback(self, request: WsgiHttpRequest) ‑> BoltResponse +def handle_callback(self,
                              request: WsgiHttpRequest) ‑> BoltResponse
                              -def handle_installation(self, request: WsgiHttpRequest) ‑> BoltResponse +def handle_installation(self,
                              request: WsgiHttpRequest) ‑> BoltResponse
                              @@ -192,7 +192,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html index b8d462a1d..37d389042 100644 --- a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html +++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.wsgi.http_request API documentation - + @@ -112,7 +112,7 @@

                              Methods

                              -def get_headers(self) ‑> Dict[str, Union[str, Sequence[str]]] +def get_headers(self) ‑> Dict[str, str | Sequence[str]]
                              @@ -152,7 +152,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_response.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/http_response.html index c2c7b9a6e..20759ad24 100644 --- a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_response.html +++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/http_response.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.wsgi.http_response API documentation - + @@ -127,7 +127,7 @@

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html index 2ff190ed0..c4df0004f 100644 --- a/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.wsgi API documentation - + @@ -56,7 +56,7 @@

                              Classes

                              class SlackRequestHandler -(app: App, path: str = '/slack/events') +(app: App,
                              path: str = '/slack/events')

                              Setup Bolt as a WSGI web framework, this will make your application compatible with WSGI web servers. @@ -163,19 +163,19 @@

                              Args

                              Methods

                              -def dispatch(self, request: WsgiHttpRequest) ‑> BoltResponse +def dispatch(self,
                              request: WsgiHttpRequest) ‑> BoltResponse
                              -def handle_callback(self, request: WsgiHttpRequest) ‑> BoltResponse +def handle_callback(self,
                              request: WsgiHttpRequest) ‑> BoltResponse
                              -def handle_installation(self, request: WsgiHttpRequest) ‑> BoltResponse +def handle_installation(self,
                              request: WsgiHttpRequest) ‑> BoltResponse
                              @@ -219,7 +219,7 @@

                              -

                              Generated by pdoc 0.11.1.

                              +

                              Generated by pdoc 0.11.3.

                              diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/internals.html b/docs/static/api-docs/slack_bolt/adapter/wsgi/internals.html index caf02c467..756cee780 100644 --- a/docs/static/api-docs/slack_bolt/adapter/wsgi/internals.html +++ b/docs/static/api-docs/slack_bolt/adapter/wsgi/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.adapter.wsgi.internals API documentation - + @@ -49,7 +49,7 @@

                              Module slack_bolt.adapter.wsgi.internals

                              diff --git a/docs/static/api-docs/slack_bolt/app/app.html b/docs/static/api-docs/slack_bolt/app/app.html index b8d19c85e..cdd50eb6f 100644 --- a/docs/static/api-docs/slack_bolt/app/app.html +++ b/docs/static/api-docs/slack_bolt/app/app.html @@ -3,13 +3,13 @@ - + slack_bolt.app.app API documentation - + @@ -37,7 +37,7 @@

                              Classes

                              class App -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None, assistant_thread_context_store: Optional[AssistantThreadContextStore] = None) +(*,
                              logger: logging.Logger | None = None,
                              name: str | None = None,
                              process_before_response: bool = False,
                              raise_error_for_unhandled_request: bool = False,
                              signing_secret: str | None = None,
                              token: str | None = None,
                              token_verification_enabled: bool = True,
                              client: slack_sdk.web.client.WebClient | None = None,
                              before_authorize: Middleware | Callable[..., Any] | None = None,
                              authorize: Callable[..., AuthorizeResult] | None = None,
                              user_facing_authorize_error_message: str | None = None,
                              installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
                              installation_store_bot_only: bool | None = None,
                              request_verification_enabled: bool = True,
                              ignoring_self_events_enabled: bool = True,
                              ignoring_self_assistant_message_events_enabled: bool = True,
                              ssl_check_enabled: bool = True,
                              url_verification_enabled: bool = True,
                              attaching_function_token_enabled: bool = True,
                              oauth_settings: OAuthSettings | None = None,
                              oauth_flow: OAuthFlow | None = None,
                              verification_token: str | None = None,
                              listener_executor: concurrent.futures._base.Executor | None = None,
                              assistant_thread_context_store: AssistantThreadContextStore | None = None)

                              Bolt App that provides functionalities to register middleware/listeners.

                              @@ -1514,7 +1514,7 @@

                              Instance variables

                              return self._client

                • -
                  prop installation_store : Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore]
                  +
                  prop installation_store : slack_sdk.oauth.installation_store.installation_store.InstallationStore | None

                  The slack_sdk.oauth.InstallationStore that can be used in the authorize middleware.

                  @@ -1566,7 +1566,7 @@

                  Instance variables

                  return self._name
                  -
                  prop oauth_flow : Optional[OAuthFlow]
                  +
                  prop oauth_flowOAuthFlow | None

                  Configured OAuthFlow object if exists.

                  @@ -1595,7 +1595,7 @@

                  Instance variables

                  Methods

                  -def action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def action(self,
                  constraints: str | Pattern | Dict[str, str | Pattern],
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new action listener. This method can be used as either a decorator or a method.

                  @@ -1626,33 +1626,33 @@

                  Args

                  -def assistant(self, assistant: Assistant) ‑> Optional[Callable] +def assistant(self,
                  assistant: Assistant) ‑> Callable | None
                  -def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def attachment_action(self,
                  callback_id: str | Pattern,
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new interactive_message action listener. Refer to https://api.slack.com/legacy/message-buttons for details.

                  -def block_action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def block_action(self,
                  constraints: str | Pattern | Dict[str, str | Pattern],
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new block_actions action listener. Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

                  -def block_suggestion(self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def block_suggestion(self,
                  action_id: str | Pattern,
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new block_suggestion listener.

                  -def command(self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def command(self,
                  command: str | Pattern,
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new slash command listener. @@ -1682,40 +1682,40 @@

                  Args

                  -def default_app_uninstalled_event_listener(self) ‑> Callable[..., Optional[BoltResponse]] +def default_app_uninstalled_event_listener(self) ‑> Callable[..., BoltResponse | None]
                  -def default_tokens_revoked_event_listener(self) ‑> Callable[..., Optional[BoltResponse]] +def default_tokens_revoked_event_listener(self) ‑> Callable[..., BoltResponse | None]
                  -def dialog_cancellation(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_cancellation(self,
                  callback_id: str | Pattern,
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new dialog_cancellation listener. Refer to https://api.slack.com/dialogs for details.

                  -def dialog_submission(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_submission(self,
                  callback_id: str | Pattern,
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new dialog_submission listener. Refer to https://api.slack.com/dialogs for details.

                  -def dialog_suggestion(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_suggestion(self,
                  callback_id: str | Pattern,
                  matchers: Sequence[Callable[..., bool]] | None = None,
                  middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                  Registers a new dialog_suggestion listener. Refer to https://api.slack.com/dialogs for details.

                  -def dispatch(self, req: BoltRequest) ‑> BoltResponse +def dispatch(self,
                  req: BoltRequest) ‑> BoltResponse

                  Applies all middleware and dispatches an incoming request from Slack to the right code path.

                  @@ -1734,7 +1734,7 @@

                  Returns

                  -def error(self, func: Callable[..., Optional[BoltResponse]]) ‑> Callable[..., Optional[BoltResponse]] +def error(self,
                  func: Callable[..., BoltResponse | None]) ‑> Callable[..., BoltResponse | None]

                  Updates the global error handler. This method can be used as either a decorator or a method.

                  @@ -1756,7 +1756,7 @@

                  Args

                -def event(self, event: Union[str, Pattern, Dict[str, Union[str, Sequence[Union[str, Pattern, ForwardRef(None)]], ForwardRef(None)]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def event(self,
                event: str | Pattern | Dict[str, str | Sequence[str | Pattern | None] | None],
                matchers: Sequence[Callable[..., bool]] | None = None,
                middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

                Registers a new event listener. This method can be used as either a decorator or a method.

                @@ -1787,7 +1787,7 @@

                Args

            -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def function(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None,
            auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new Function listener. @@ -1820,13 +1820,13 @@

            Args

            -def global_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def global_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new global shortcut listener.

            -def message(self, keyword: Union[str, Pattern] = '', matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def message(self,
            keyword: str | Pattern = '',
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new message event listener. This method can be used as either a decorator or a method. @@ -1855,13 +1855,13 @@

            Args

            -def message_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def message_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new message shortcut listener.

            -def middleware(self, *args) ‑> Optional[Callable] +def middleware(self, *args) ‑> Callable | None

            Registers a new middleware to this app. @@ -1884,7 +1884,7 @@

            Args

            -def options(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def options(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new options listener. @@ -1924,7 +1924,7 @@

            Args

            -def shortcut(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def shortcut(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new shortcut listener. @@ -1960,7 +1960,7 @@

            Args

            -def start(self, port: int = 3000, path: str = '/slack/events', http_server_logger_enabled: bool = True) ‑> None +def start(self,
            port: int = 3000,
            path: str = '/slack/events',
            http_server_logger_enabled: bool = True) ‑> None

            Starts a web server for local development.

            @@ -1981,7 +1981,7 @@

            Args

            -def step(self, callback_id: Union[str, Pattern, WorkflowStepWorkflowStepBuilder], edit: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None, save: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None, execute: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None) +def step(self,
            callback_id: str | Pattern | WorkflowStep | WorkflowStepBuilder,
            edit: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None,
            save: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None,
            execute: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None)

            Deprecated

            @@ -2019,14 +2019,14 @@

            Args

            -def use(self, *args) ‑> Optional[Callable] +def use(self, *args) ‑> Callable | None

            Registers a new global middleware to this app. This method can be used as either a decorator or a method.

            Refer to App#middleware() method's docstring for details.

            -def view(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new view_submission/view_closed event listener. @@ -2066,14 +2066,14 @@

            Args

            -def view_closed(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view_closed(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new view_closed listener. Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

            -def view_submission(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view_submission(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new view_submission listener. @@ -2083,7 +2083,7 @@

            Args

            class SlackAppDevelopmentServer -(port: int, path: str, app: App, oauth_flow: Optional[OAuthFlow] = None, http_server_logger_enabled: bool = True) +(port: int,
            path: str,
            app: App,
            oauth_flow: OAuthFlow | None = None,
            http_server_logger_enabled: bool = True)

            Slack App Development Server

            @@ -2312,7 +2312,7 @@

            -

            Generated by pdoc 0.11.1.

            +

            Generated by pdoc 0.11.3.

            diff --git a/docs/static/api-docs/slack_bolt/app/async_app.html b/docs/static/api-docs/slack_bolt/app/async_app.html index 837f4befe..a96fd4707 100644 --- a/docs/static/api-docs/slack_bolt/app/async_app.html +++ b/docs/static/api-docs/slack_bolt/app/async_app.html @@ -3,13 +3,13 @@ - + slack_bolt.app.async_app API documentation - + @@ -37,7 +37,7 @@

            Classes

            class AsyncApp -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, before_authorize: Union[AsyncMiddleware, Callable[..., Awaitable[Any]], ForwardRef(None)] = None, authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[AsyncOAuthSettings] = None, oauth_flow: Optional[AsyncOAuthFlow] = None, verification_token: Optional[str] = None, assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None) +(*,
            logger: logging.Logger | None = None,
            name: str | None = None,
            process_before_response: bool = False,
            raise_error_for_unhandled_request: bool = False,
            signing_secret: str | None = None,
            token: str | None = None,
            client: slack_sdk.web.async_client.AsyncWebClient | None = None,
            before_authorize: AsyncMiddleware | Callable[..., Awaitable[Any]] | None = None,
            authorize: Callable[..., Awaitable[AuthorizeResult]] | None = None,
            user_facing_authorize_error_message: str | None = None,
            installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
            installation_store_bot_only: bool | None = None,
            request_verification_enabled: bool = True,
            ignoring_self_events_enabled: bool = True,
            ignoring_self_assistant_message_events_enabled: bool = True,
            ssl_check_enabled: bool = True,
            url_verification_enabled: bool = True,
            attaching_function_token_enabled: bool = True,
            oauth_settings: AsyncOAuthSettings | None = None,
            oauth_flow: AsyncOAuthFlow | None = None,
            verification_token: str | None = None,
            assistant_thread_context_store: AsyncAssistantThreadContextStore | None = None)

            Bolt App that provides functionalities to register middleware/listeners.

            @@ -1547,7 +1547,7 @@

            Instance variables

            return self._async_client
            -
            prop installation_store : Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore]
            +
            prop installation_store : slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None

            The slack_sdk.oauth.AsyncInstallationStore that can be used in the authorize middleware.

            @@ -1599,7 +1599,7 @@

            Instance variables

            return self._name
            -
            prop oauth_flow : Optional[AsyncOAuthFlow]
            +
            prop oauth_flowAsyncOAuthFlow | None

            Configured OAuthFlow object if exists.

            @@ -1628,7 +1628,7 @@

            Instance variables

            Methods

            -def action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def action(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new action listener. This method can be used as either a decorator or a method.

            @@ -1659,13 +1659,13 @@

            Args

            -def assistant(self, assistant: AsyncAssistant) ‑> Optional[Callable] +def assistant(self,
            assistant: AsyncAssistant) ‑> Callable | None
            -async def async_dispatch(self, req: AsyncBoltRequest) ‑> BoltResponse +async def async_dispatch(self,
            req: AsyncBoltRequest) ‑> BoltResponse

            Applies all middleware and dispatches an incoming request from Slack to the right code path.

            @@ -1678,27 +1678,27 @@

            Returns

            The response generated by this Bolt app.

            -def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def attachment_action(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new interactive_message action listener. Refer to https://api.slack.com/legacy/message-buttons for details.

            -def block_action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def block_action(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new block_actions action listener. Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

            -def block_suggestion(self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def block_suggestion(self,
            action_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new block_suggestion listener.

            -def command(self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def command(self,
            command: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new slash command listener. @@ -1728,33 +1728,33 @@

            Args

            -def default_app_uninstalled_event_listener(self) ‑> Callable[..., Awaitable[Optional[BoltResponse]]] +def default_app_uninstalled_event_listener(self) ‑> Callable[..., Awaitable[BoltResponse | None]]
            -def default_tokens_revoked_event_listener(self) ‑> Callable[..., Awaitable[Optional[BoltResponse]]] +def default_tokens_revoked_event_listener(self) ‑> Callable[..., Awaitable[BoltResponse | None]]
            -def dialog_cancellation(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def dialog_cancellation(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new dialog_submission listener. Refer to https://api.slack.com/dialogs for details.

            -def dialog_submission(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def dialog_submission(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new dialog_submission listener. Refer to https://api.slack.com/dialogs for details.

            -def dialog_suggestion(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def dialog_suggestion(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new dialog_suggestion listener. @@ -1767,7 +1767,7 @@

            Args

            -def error(self, func: Callable[..., Awaitable[Optional[BoltResponse]]]) ‑> Callable[..., Awaitable[Optional[BoltResponse]]] +def error(self,
            func: Callable[..., Awaitable[BoltResponse | None]]) ‑> Callable[..., Awaitable[BoltResponse | None]]

            Updates the global error handler. This method can be used as either a decorator or a method.

            @@ -1789,7 +1789,7 @@

            Args

            -def event(self, event: Union[str, Pattern, Dict[str, Union[str, Sequence[Union[str, Pattern, ForwardRef(None)]], ForwardRef(None)]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def event(self,
            event: str | Pattern | Dict[str, str | Sequence[str | Pattern | None] | None],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new event listener. This method can be used as either a decorator or a method.

            @@ -1820,7 +1820,7 @@

            Args

            -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]] +def function(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None,
            auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., Awaitable[BoltResponse]] | None]

            Registers a new Function listener. @@ -1853,13 +1853,13 @@

            Args

            -def global_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def global_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new global shortcut listener.

            -def message(self, keyword: Union[str, Pattern] = '', matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def message(self,
            keyword: str | Pattern = '',
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new message event listener. This method can be used as either a decorator or a method. @@ -1888,13 +1888,13 @@

            Args

            -def message_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def message_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new message shortcut listener.

            -def middleware(self, *args) ‑> Optional[Callable] +def middleware(self, *args) ‑> Callable | None

            Registers a new middleware to this app. @@ -1916,7 +1916,7 @@

            Args

            -def options(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def options(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new options listener. @@ -1956,7 +1956,7 @@

            Args

            -def server(self, port: int = 3000, path: str = '/slack/events', host: Optional[str] = None) ‑> AsyncSlackAppServer +def server(self, port: int = 3000, path: str = '/slack/events', host: str | None = None) ‑> AsyncSlackAppServer

            Configure a web server using AIOHTTP. @@ -1972,7 +1972,7 @@

            Args

            -def shortcut(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def shortcut(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new shortcut listener. @@ -2008,7 +2008,7 @@

            Args

            -def start(self, port: int = 3000, path: str = '/slack/events', host: Optional[str] = None) ‑> None +def start(self, port: int = 3000, path: str = '/slack/events', host: str | None = None) ‑> None

            Start a web server using AIOHTTP. @@ -2024,7 +2024,7 @@

            Args

            -def step(self, callback_id: Union[str, Pattern, AsyncWorkflowStepAsyncWorkflowStepBuilder], edit: Union[Callable[..., Optional[BoltResponse]], AsyncListener, Sequence[Callable], ForwardRef(None)] = None, save: Union[Callable[..., Optional[BoltResponse]], AsyncListener, Sequence[Callable], ForwardRef(None)] = None, execute: Union[Callable[..., Optional[BoltResponse]], AsyncListener, Sequence[Callable], ForwardRef(None)] = None) +def step(self,
            callback_id: str | Pattern | AsyncWorkflowStep | AsyncWorkflowStepBuilder,
            edit: Callable[..., BoltResponse | None] | AsyncListener | Sequence[Callable] | None = None,
            save: Callable[..., BoltResponse | None] | AsyncListener | Sequence[Callable] | None = None,
            execute: Callable[..., BoltResponse | None] | AsyncListener | Sequence[Callable] | None = None)

            Deprecated

            @@ -2062,13 +2062,13 @@

            Args

            -def use(self, *args) ‑> Optional[Callable] +def use(self, *args) ‑> Callable | None

            Refer to AsyncApp#middleware() method's docstring for details.

            -def view(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def view(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new view_submission/view_closed event listener. @@ -2108,14 +2108,14 @@

            Args

            -def view_closed(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def view_closed(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new view_closed listener. Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

            -def view_submission(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def view_submission(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new view_submission listener. @@ -2213,7 +2213,7 @@

            -

            Generated by pdoc 0.11.1.

            +

            Generated by pdoc 0.11.3.

            diff --git a/docs/static/api-docs/slack_bolt/app/async_server.html b/docs/static/api-docs/slack_bolt/app/async_server.html index 8e46004ad..a52a781ee 100644 --- a/docs/static/api-docs/slack_bolt/app/async_server.html +++ b/docs/static/api-docs/slack_bolt/app/async_server.html @@ -3,13 +3,13 @@ - + slack_bolt.app.async_server API documentation - + @@ -37,7 +37,7 @@

            Classes

            class AsyncSlackAppServer -(port: int, path: str, app: AsyncApp, host: Optional[str] = None) +(port: int, path: str, app: AsyncApp, host: str | None = None)

            Standalone AIOHTTP Web Server. @@ -172,7 +172,7 @@

            Methods

            -def start(self, host: Optional[str] = None) ‑> None +def start(self, host: str | None = None) ‑> None

            Starts a new web server process.

            @@ -213,7 +213,7 @@

            -

            Generated by pdoc 0.11.1.

            +

            Generated by pdoc 0.11.3.

            diff --git a/docs/static/api-docs/slack_bolt/app/index.html b/docs/static/api-docs/slack_bolt/app/index.html index b43977c61..ef174a0a9 100644 --- a/docs/static/api-docs/slack_bolt/app/index.html +++ b/docs/static/api-docs/slack_bolt/app/index.html @@ -3,13 +3,13 @@ - + slack_bolt.app API documentation - + @@ -56,7 +56,7 @@

            Classes

            class App -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None, assistant_thread_context_store: Optional[AssistantThreadContextStore] = None) +(*,
            logger: logging.Logger | None = None,
            name: str | None = None,
            process_before_response: bool = False,
            raise_error_for_unhandled_request: bool = False,
            signing_secret: str | None = None,
            token: str | None = None,
            token_verification_enabled: bool = True,
            client: slack_sdk.web.client.WebClient | None = None,
            before_authorize: Middleware | Callable[..., Any] | None = None,
            authorize: Callable[..., AuthorizeResult] | None = None,
            user_facing_authorize_error_message: str | None = None,
            installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
            installation_store_bot_only: bool | None = None,
            request_verification_enabled: bool = True,
            ignoring_self_events_enabled: bool = True,
            ignoring_self_assistant_message_events_enabled: bool = True,
            ssl_check_enabled: bool = True,
            url_verification_enabled: bool = True,
            attaching_function_token_enabled: bool = True,
            oauth_settings: OAuthSettings | None = None,
            oauth_flow: OAuthFlow | None = None,
            verification_token: str | None = None,
            listener_executor: concurrent.futures._base.Executor | None = None,
            assistant_thread_context_store: AssistantThreadContextStore | None = None)

            Bolt App that provides functionalities to register middleware/listeners.

            @@ -1533,7 +1533,7 @@

            Instance variables

            return self._client
            -
            prop installation_store : Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore]
            +
            prop installation_store : slack_sdk.oauth.installation_store.installation_store.InstallationStore | None

            The slack_sdk.oauth.InstallationStore that can be used in the authorize middleware.

            @@ -1585,7 +1585,7 @@

            Instance variables

            return self._name
            -
            prop oauth_flow : Optional[OAuthFlow]
            +
            prop oauth_flowOAuthFlow | None

            Configured OAuthFlow object if exists.

            @@ -1614,7 +1614,7 @@

            Instance variables

            Methods

            -def action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def action(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new action listener. This method can be used as either a decorator or a method.

            @@ -1645,33 +1645,33 @@

            Args

            -def assistant(self, assistant: Assistant) ‑> Optional[Callable] +def assistant(self,
            assistant: Assistant) ‑> Callable | None
            -def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def attachment_action(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new interactive_message action listener. Refer to https://api.slack.com/legacy/message-buttons for details.

            -def block_action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def block_action(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new block_actions action listener. Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

            -def block_suggestion(self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def block_suggestion(self,
            action_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new block_suggestion listener.

            -def command(self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def command(self,
            command: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new slash command listener. @@ -1701,40 +1701,40 @@

            Args

            -def default_app_uninstalled_event_listener(self) ‑> Callable[..., Optional[BoltResponse]] +def default_app_uninstalled_event_listener(self) ‑> Callable[..., BoltResponse | None]
            -def default_tokens_revoked_event_listener(self) ‑> Callable[..., Optional[BoltResponse]] +def default_tokens_revoked_event_listener(self) ‑> Callable[..., BoltResponse | None]
            -def dialog_cancellation(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_cancellation(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new dialog_cancellation listener. Refer to https://api.slack.com/dialogs for details.

            -def dialog_submission(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_submission(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new dialog_submission listener. Refer to https://api.slack.com/dialogs for details.

            -def dialog_suggestion(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_suggestion(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new dialog_suggestion listener. Refer to https://api.slack.com/dialogs for details.

            -def dispatch(self, req: BoltRequest) ‑> BoltResponse +def dispatch(self,
            req: BoltRequest) ‑> BoltResponse

            Applies all middleware and dispatches an incoming request from Slack to the right code path.

            @@ -1753,7 +1753,7 @@

            Returns

            -def error(self, func: Callable[..., Optional[BoltResponse]]) ‑> Callable[..., Optional[BoltResponse]] +def error(self,
            func: Callable[..., BoltResponse | None]) ‑> Callable[..., BoltResponse | None]

            Updates the global error handler. This method can be used as either a decorator or a method.

            @@ -1775,7 +1775,7 @@

            Args

            -def event(self, event: Union[str, Pattern, Dict[str, Union[str, Sequence[Union[str, Pattern, ForwardRef(None)]], ForwardRef(None)]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def event(self,
            event: str | Pattern | Dict[str, str | Sequence[str | Pattern | None] | None],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new event listener. This method can be used as either a decorator or a method.

            @@ -1806,7 +1806,7 @@

            Args

            -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def function(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None,
            auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new Function listener. @@ -1839,13 +1839,13 @@

            Args

            -def global_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def global_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new global shortcut listener.

            -def message(self, keyword: Union[str, Pattern] = '', matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def message(self,
            keyword: str | Pattern = '',
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new message event listener. This method can be used as either a decorator or a method. @@ -1874,13 +1874,13 @@

            Args

            -def message_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def message_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new message shortcut listener.

            -def middleware(self, *args) ‑> Optional[Callable] +def middleware(self, *args) ‑> Callable | None

            Registers a new middleware to this app. @@ -1903,7 +1903,7 @@

            Args

            -def options(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def options(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new options listener. @@ -1943,7 +1943,7 @@

            Args

            -def shortcut(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def shortcut(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new shortcut listener. @@ -1979,7 +1979,7 @@

            Args

            -def start(self, port: int = 3000, path: str = '/slack/events', http_server_logger_enabled: bool = True) ‑> None +def start(self,
            port: int = 3000,
            path: str = '/slack/events',
            http_server_logger_enabled: bool = True) ‑> None

            Starts a web server for local development.

            @@ -2000,7 +2000,7 @@

            Args

            -def step(self, callback_id: Union[str, Pattern, WorkflowStepWorkflowStepBuilder], edit: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None, save: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None, execute: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None) +def step(self,
            callback_id: str | Pattern | WorkflowStep | WorkflowStepBuilder,
            edit: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None,
            save: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None,
            execute: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None)

            Deprecated

            @@ -2038,14 +2038,14 @@

            Args

            -def use(self, *args) ‑> Optional[Callable] +def use(self, *args) ‑> Callable | None

            Registers a new global middleware to this app. This method can be used as either a decorator or a method.

            Refer to App#middleware() method's docstring for details.

            -def view(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new view_submission/view_closed event listener. @@ -2085,14 +2085,14 @@

            Args

            -def view_closed(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view_closed(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new view_closed listener. Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

            -def view_submission(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view_submission(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., bool]] | None = None,
            middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

            Registers a new view_submission listener. @@ -2168,7 +2168,7 @@

            App diff --git a/docs/static/api-docs/slack_bolt/async_app.html b/docs/static/api-docs/slack_bolt/async_app.html index 13d767100..f3147b7d3 100644 --- a/docs/static/api-docs/slack_bolt/async_app.html +++ b/docs/static/api-docs/slack_bolt/async_app.html @@ -3,13 +3,13 @@ - + slack_bolt.async_app API documentation - + @@ -120,7 +120,7 @@

            Classes

            Class variables

            -
            var response : Optional[BoltResponse]
            +
            var responseBoltResponse | None
            @@ -128,7 +128,7 @@

            Class variables

            class AsyncApp -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, before_authorize: Union[AsyncMiddleware, Callable[..., Awaitable[Any]], ForwardRef(None)] = None, authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[AsyncOAuthSettings] = None, oauth_flow: Optional[AsyncOAuthFlow] = None, verification_token: Optional[str] = None, assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None) +(*,
            logger: logging.Logger | None = None,
            name: str | None = None,
            process_before_response: bool = False,
            raise_error_for_unhandled_request: bool = False,
            signing_secret: str | None = None,
            token: str | None = None,
            client: slack_sdk.web.async_client.AsyncWebClient | None = None,
            before_authorize: AsyncMiddleware | Callable[..., Awaitable[Any]] | None = None,
            authorize: Callable[..., Awaitable[AuthorizeResult]] | None = None,
            user_facing_authorize_error_message: str | None = None,
            installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
            installation_store_bot_only: bool | None = None,
            request_verification_enabled: bool = True,
            ignoring_self_events_enabled: bool = True,
            ignoring_self_assistant_message_events_enabled: bool = True,
            ssl_check_enabled: bool = True,
            url_verification_enabled: bool = True,
            attaching_function_token_enabled: bool = True,
            oauth_settings: AsyncOAuthSettings | None = None,
            oauth_flow: AsyncOAuthFlow | None = None,
            verification_token: str | None = None,
            assistant_thread_context_store: AsyncAssistantThreadContextStore | None = None)

            Bolt App that provides functionalities to register middleware/listeners.

            @@ -1638,7 +1638,7 @@

            Instance variables

            return self._async_client
            -
            prop installation_store : Optional[slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore]
            +
            prop installation_store : slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None

            The slack_sdk.oauth.AsyncInstallationStore that can be used in the authorize middleware.

            @@ -1690,7 +1690,7 @@

            Instance variables

            return self._name
            -
            prop oauth_flow : Optional[AsyncOAuthFlow]
            +
            prop oauth_flowAsyncOAuthFlow | None

            Configured OAuthFlow object if exists.

            @@ -1719,7 +1719,7 @@

            Instance variables

            Methods

            -def action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def action(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new action listener. This method can be used as either a decorator or a method.

            @@ -1750,13 +1750,13 @@

            Args

            -def assistant(self, assistant: AsyncAssistant) ‑> Optional[Callable] +def assistant(self,
            assistant: AsyncAssistant) ‑> Callable | None
            -async def async_dispatch(self, req: AsyncBoltRequest) ‑> BoltResponse +async def async_dispatch(self,
            req: AsyncBoltRequest) ‑> BoltResponse

            Applies all middleware and dispatches an incoming request from Slack to the right code path.

            @@ -1769,27 +1769,27 @@

            Returns

            The response generated by this Bolt app.

            -def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def attachment_action(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new interactive_message action listener. Refer to https://api.slack.com/legacy/message-buttons for details.

            -def block_action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def block_action(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new block_actions action listener. Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

            -def block_suggestion(self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def block_suggestion(self,
            action_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new block_suggestion listener.

            -def command(self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def command(self,
            command: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new slash command listener. @@ -1819,33 +1819,33 @@

            Args

            -def default_app_uninstalled_event_listener(self) ‑> Callable[..., Awaitable[Optional[BoltResponse]]] +def default_app_uninstalled_event_listener(self) ‑> Callable[..., Awaitable[BoltResponse | None]]
            -def default_tokens_revoked_event_listener(self) ‑> Callable[..., Awaitable[Optional[BoltResponse]]] +def default_tokens_revoked_event_listener(self) ‑> Callable[..., Awaitable[BoltResponse | None]]
            -def dialog_cancellation(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def dialog_cancellation(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new dialog_submission listener. Refer to https://api.slack.com/dialogs for details.

            -def dialog_submission(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def dialog_submission(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new dialog_submission listener. Refer to https://api.slack.com/dialogs for details.

            -def dialog_suggestion(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def dialog_suggestion(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new dialog_suggestion listener. @@ -1858,7 +1858,7 @@

            Args

            -def error(self, func: Callable[..., Awaitable[Optional[BoltResponse]]]) ‑> Callable[..., Awaitable[Optional[BoltResponse]]] +def error(self,
            func: Callable[..., Awaitable[BoltResponse | None]]) ‑> Callable[..., Awaitable[BoltResponse | None]]

            Updates the global error handler. This method can be used as either a decorator or a method.

            @@ -1880,7 +1880,7 @@

            Args

            -def event(self, event: Union[str, Pattern, Dict[str, Union[str, Sequence[Union[str, Pattern, ForwardRef(None)]], ForwardRef(None)]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def event(self,
            event: str | Pattern | Dict[str, str | Sequence[str | Pattern | None] | None],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new event listener. This method can be used as either a decorator or a method.

            @@ -1911,7 +1911,7 @@

            Args

            -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]] +def function(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None,
            auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., Awaitable[BoltResponse]] | None]

            Registers a new Function listener. @@ -1944,13 +1944,13 @@

            Args

            -def global_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def global_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new global shortcut listener.

            -def message(self, keyword: Union[str, Pattern] = '', matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def message(self,
            keyword: str | Pattern = '',
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new message event listener. This method can be used as either a decorator or a method. @@ -1979,13 +1979,13 @@

            Args

            -def message_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def message_shortcut(self,
            callback_id: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new message shortcut listener.

            -def middleware(self, *args) ‑> Optional[Callable] +def middleware(self, *args) ‑> Callable | None

            Registers a new middleware to this app. @@ -2007,7 +2007,7 @@

            Args

            -def options(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def options(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new options listener. @@ -2047,7 +2047,7 @@

            Args

            -def server(self, port: int = 3000, path: str = '/slack/events', host: Optional[str] = None) ‑> AsyncSlackAppServer +def server(self, port: int = 3000, path: str = '/slack/events', host: str | None = None) ‑> AsyncSlackAppServer

            Configure a web server using AIOHTTP. @@ -2063,7 +2063,7 @@

            Args

            -def shortcut(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def shortcut(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new shortcut listener. @@ -2099,7 +2099,7 @@

            Args

            -def start(self, port: int = 3000, path: str = '/slack/events', host: Optional[str] = None) ‑> None +def start(self, port: int = 3000, path: str = '/slack/events', host: str | None = None) ‑> None

            Start a web server using AIOHTTP. @@ -2115,7 +2115,7 @@

            Args

            -def step(self, callback_id: Union[str, Pattern, AsyncWorkflowStepAsyncWorkflowStepBuilder], edit: Union[Callable[..., Optional[BoltResponse]], AsyncListener, Sequence[Callable], ForwardRef(None)] = None, save: Union[Callable[..., Optional[BoltResponse]], AsyncListener, Sequence[Callable], ForwardRef(None)] = None, execute: Union[Callable[..., Optional[BoltResponse]], AsyncListener, Sequence[Callable], ForwardRef(None)] = None) +def step(self,
            callback_id: str | Pattern | AsyncWorkflowStep | AsyncWorkflowStepBuilder,
            edit: Callable[..., BoltResponse | None] | AsyncListener | Sequence[Callable] | None = None,
            save: Callable[..., BoltResponse | None] | AsyncListener | Sequence[Callable] | None = None,
            execute: Callable[..., BoltResponse | None] | AsyncListener | Sequence[Callable] | None = None)

            Deprecated

            @@ -2153,13 +2153,13 @@

            Args

            -def use(self, *args) ‑> Optional[Callable] +def use(self, *args) ‑> Callable | None

            Refer to AsyncApp#middleware() method's docstring for details.

            -def view(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def view(self,
            constraints: str | Pattern | Dict[str, str | Pattern],
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new view_submission/view_closed event listener. @@ -2199,14 +2199,14 @@

            Args

            -def view_closed(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def view_closed(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new view_closed listener. Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

            -def view_submission(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None) ‑> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]] +def view_submission(self,
            constraints: str | Pattern,
            matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
            middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]

            Registers a new view_submission listener. @@ -2242,7 +2242,7 @@

            Args

            class AsyncAssistant -(*, app_name: str = 'assistant', thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +(*,
            app_name: str = 'assistant',
            thread_context_store: AsyncAssistantThreadContextStore | None = None,
            logger: logging.Logger | None = None)

            A middleware can process request data before other middleware and listener functions.

            @@ -2551,11 +2551,11 @@

            Ancestors

          Class variables

          -
          var base_logger : Optional[logging.Logger]
          +
          var base_logger : logging.Logger | None
          -
          var thread_context_store : Optional[AsyncAssistantThreadContextStore]
          +
          var thread_context_storeAsyncAssistantThreadContextStore | None
          @@ -2563,7 +2563,7 @@

          Class variables

          Static methods

          -async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict) +async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext,
          payload: dict)
          @@ -2572,31 +2572,31 @@

          Static methods

          Methods

          -def bot_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def bot_message(self,
          *args,
          matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
          middleware: Callable | AsyncMiddleware | None = None,
          lazy: List[Callable[..., None]] | None = None)
          -def build_listener(self, listener_or_functions: Union[AsyncListener, Callable, List[Callable]], matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]] = None, middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> AsyncListener +def build_listener(self,
          listener_or_functions: AsyncListener | Callable | List[Callable],
          matchers: List[AsyncListenerMatcher | Callable[..., Awaitable[bool]]] | None = None,
          middleware: List[AsyncMiddleware] | None = None,
          base_logger: logging.Logger | None = None) ‑> AsyncListener
          -def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_context_changed(self,
          *args,
          matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
          middleware: Callable | AsyncMiddleware | None = None,
          lazy: List[Callable[..., None]] | None = None)
          -def thread_started(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_started(self,
          *args,
          matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
          middleware: Callable | AsyncMiddleware | None = None,
          lazy: List[Callable[..., None]] | None = None)
          -def user_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def user_message(self,
          *args,
          matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
          middleware: Callable | AsyncMiddleware | None = None,
          lazy: List[Callable[..., None]] | None = None)
          @@ -3004,7 +3004,7 @@

          Returns

          return self["fail"]
          -
          prop get_thread_context : Optional[AsyncGetThreadContext]
          +
          prop get_thread_contextAsyncGetThreadContext | None
          @@ -3029,7 +3029,7 @@

          Returns

          return self["listener_runner"]
          -
          prop respond : Optional[AsyncRespond]
          +
          prop respondAsyncRespond | None

          respond() function for this request.

          @app.action("button")
          @@ -3076,7 +3076,7 @@ 

          Returns

          return self["respond"]
          -
          prop save_thread_context : Optional[AsyncSaveThreadContext]
          +
          prop save_thread_contextAsyncSaveThreadContext | None
          @@ -3131,7 +3131,7 @@

          Returns

          return self["say"]
          -
          prop set_status : Optional[AsyncSetStatus]
          +
          prop set_statusAsyncSetStatus | None
          @@ -3143,7 +3143,7 @@

          Returns

          return self.get("set_status")
          -
          prop set_suggested_prompts : Optional[AsyncSetSuggestedPrompts]
          +
          prop set_suggested_promptsAsyncSetSuggestedPrompts | None
          @@ -3155,7 +3155,7 @@

          Returns

          return self.get("set_suggested_prompts")
          -
          prop set_title : Optional[AsyncSetTitle]
          +
          prop set_titleAsyncSetTitle | None
          @@ -3208,7 +3208,7 @@

          Inherited members

          class AsyncBoltRequest -(*, body: Union[str, dict], query: Union[str, Dict[str, str], Dict[str, Sequence[str]], ForwardRef(None)] = None, headers: Optional[Dict[str, Union[str, Sequence[str]]]] = None, context: Optional[Dict[str, Any]] = None, mode: str = 'http') +(*,
          body: str | dict,
          query: str | Dict[str, str] | Dict[str, Sequence[str]] | None = None,
          headers: Dict[str, str | Sequence[str]] | None = None,
          context: Dict[str, Any] | None = None,
          mode: str = 'http')

          Request to a Bolt app.

          @@ -3305,7 +3305,7 @@

          Class variables

          -
          var content_type : Optional[str]
          +
          var content_type : str | None
          @@ -3317,7 +3317,7 @@

          Class variables

          -
          var lazy_function_name : Optional[str]
          +
          var lazy_function_name : str | None
          @@ -3350,7 +3350,7 @@

          Methods

          class AsyncCustomListenerMatcher -(*, app_name: str, func: Callable[..., Awaitable[bool]], base_logger: Optional[logging.Logger] = None) +(*,
          app_name: str,
          func: Callable[..., Awaitable[bool]],
          base_logger: logging.Logger | None = None)
          @@ -3415,7 +3415,7 @@

          Inherited members

          class AsyncGetThreadContext -(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +(thread_context_store: AsyncAssistantThreadContextStore,
          channel_id: str,
          thread_ts: str,
          payload: dict)
          @@ -3589,13 +3589,13 @@

          Class variables

          Methods

          -async def async_matches(self, *, req: AsyncBoltRequest, resp: BoltResponse) ‑> bool +async def async_matches(self,
          *,
          req: AsyncBoltRequest,
          resp: BoltResponse) ‑> bool
          -async def run_ack_function(self, *, request: AsyncBoltRequest, response: BoltResponse) ‑> Optional[BoltResponse] +async def run_ack_function(self,
          *,
          request: AsyncBoltRequest,
          response: BoltResponse) ‑> BoltResponse | None

          Runs all the registered middleware and then run the listener function.

          @@ -3610,7 +3610,7 @@

          Returns

          The processed response

          -async def run_async_middleware(self, *, req: AsyncBoltRequest, resp: BoltResponse) ‑> Tuple[Optional[BoltResponse], bool] +async def run_async_middleware(self,
          *,
          req: AsyncBoltRequest,
          resp: BoltResponse) ‑> Tuple[BoltResponse | None, bool]

          Runs an async middleware.

          @@ -3628,7 +3628,7 @@

          Returns

          class AsyncRespond -(*, response_url: Optional[str], proxy: Optional[str] = None, ssl: Optional[ssl.SSLContext] = None) +(*,
          response_url: str | None,
          proxy: str | None = None,
          ssl: ssl.SSLContext | None = None)
          @@ -3697,15 +3697,15 @@

          Returns

          Class variables

          -
          var proxy : Optional[str]
          +
          var proxy : str | None
          -
          var response_url : Optional[str]
          +
          var response_url : str | None
          -
          var ssl : Optional[ssl.SSLContext]
          +
          var ssl : ssl.SSLContext | None
          @@ -3713,7 +3713,7 @@

          Class variables

          class AsyncSaveThreadContext -(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str) +(thread_context_store: AsyncAssistantThreadContextStore,
          channel_id: str,
          thread_ts: str)
          @@ -3761,7 +3761,7 @@

          Class variables

          class AsyncSay -(client: Optional[slack_sdk.web.async_client.AsyncWebClient], channel: Optional[str], thread_ts: Optional[str] = None, build_metadata: Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]] = None) +(client: slack_sdk.web.async_client.AsyncWebClient | None,
          channel: str | None,
          thread_ts: str | None = None,
          build_metadata: Callable[[], Awaitable[Dict | slack_sdk.models.metadata.Metadata]] | None = None)
          @@ -3848,19 +3848,19 @@

          Class variables

          Class variables

          -
          var build_metadata : Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]]
          +
          var build_metadata : Callable[[], Awaitable[Dict | slack_sdk.models.metadata.Metadata]] | None
          -
          var channel : Optional[str]
          +
          var channel : str | None
          -
          var client : Optional[slack_sdk.web.async_client.AsyncWebClient]
          +
          var client : slack_sdk.web.async_client.AsyncWebClient | None
          -
          var thread_ts : Optional[str]
          +
          var thread_ts : str | None
          @@ -3868,7 +3868,7 @@

          Class variables

          class AsyncSetStatus -(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.async_client.AsyncWebClient,
          channel_id: str,
          thread_ts: str)
          @@ -3916,7 +3916,7 @@

          Class variables

          class AsyncSetSuggestedPrompts -(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.async_client.AsyncWebClient,
          channel_id: str,
          thread_ts: str)
          @@ -3939,7 +3939,11 @@

          Class variables

          self.channel_id = channel_id self.thread_ts = thread_ts - async def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> AsyncSlackResponse: + async def __call__( + self, + prompts: List[Union[str, Dict[str, str]]], + title: Optional[str] = None, + ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): @@ -3951,6 +3955,7 @@

          Class variables

          channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, + title=title, )

          Class variables

          @@ -3971,7 +3976,7 @@

          Class variables

          class AsyncSetTitle -(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.async_client.AsyncWebClient,
          channel_id: str,
          thread_ts: str)
          @@ -4216,7 +4221,7 @@

          -

          Generated by pdoc 0.11.1.

          +

          Generated by pdoc 0.11.3.

          diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize.html b/docs/static/api-docs/slack_bolt/authorization/async_authorize.html index 392225e1f..7012e07b3 100644 --- a/docs/static/api-docs/slack_bolt/authorization/async_authorize.html +++ b/docs/static/api-docs/slack_bolt/authorization/async_authorize.html @@ -3,13 +3,13 @@ - + slack_bolt.authorization.async_authorize API documentation - + @@ -74,7 +74,7 @@

          Subclasses

          class AsyncCallableAuthorize -(*, logger: logging.Logger, func: Callable[..., Awaitable[AuthorizeResult]]) +(*,
          logger: logging.Logger,
          func: Callable[..., Awaitable[AuthorizeResult]])

          When you pass the authorize argument in AsyncApp constructor, @@ -156,7 +156,7 @@

          Ancestors

          class AsyncInstallationStoreAuthorize -(*, logger: logging.Logger, installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore, client_id: Optional[str] = None, client_secret: Optional[str] = None, token_rotation_expiration_minutes: Optional[int] = None, bot_only: bool = False, cache_enabled: bool = False, client: Optional[slack_sdk.web.async_client.AsyncWebClient] = None, user_token_resolution: str = 'authed_user') +(*,
          logger: logging.Logger,
          installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore,
          client_id: str | None = None,
          client_secret: str | None = None,
          token_rotation_expiration_minutes: int | None = None,
          bot_only: bool = False,
          cache_enabled: bool = False,
          client: slack_sdk.web.async_client.AsyncWebClient | None = None,
          user_token_resolution: str = 'authed_user')

          If you use the OAuth flow settings, this authorize implementation will be used. @@ -451,15 +451,15 @@

          Class variables

          -
          var find_bot_available : Optional[bool]
          +
          var find_bot_available : bool | None
          -
          var find_installation_available : Optional[bool]
          +
          var find_installation_available : bool | None
          -
          var token_rotator : Optional[slack_sdk.oauth.token_rotation.async_rotator.AsyncTokenRotator]
          +
          var token_rotator : slack_sdk.oauth.token_rotation.async_rotator.AsyncTokenRotator | None
          @@ -507,7 +507,7 @@

          -

          Generated by pdoc 0.11.1.

          +

          Generated by pdoc 0.11.3.

          diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html b/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html index e93200864..2b1ea126c 100644 --- a/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html +++ b/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html @@ -3,13 +3,13 @@ - + slack_bolt.authorization.async_authorize_args API documentation - + @@ -37,7 +37,7 @@

          Classes

          class AsyncAuthorizeArgs -(*, context: AsyncBoltContext, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str]) +(*,
          context: AsyncBoltContext,
          enterprise_id: str | None,
          team_id: str | None,
          user_id: str | None)

          The full list of the arguments passed to authorize function.

          @@ -97,7 +97,7 @@

          Class variables

          -
          var enterprise_id : Optional[str]
          +
          var enterprise_id : str | None
          @@ -105,11 +105,11 @@

          Class variables

          -
          var team_id : Optional[str]
          +
          var team_id : str | None
          -
          var user_id : Optional[str]
          +
          var user_id : str | None
          @@ -147,7 +147,7 @@

          -

          Generated by pdoc 0.11.1.

          +

          Generated by pdoc 0.11.3.

          diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize.html b/docs/static/api-docs/slack_bolt/authorization/authorize.html index 6d5e5526e..b838b659e 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize.html @@ -3,13 +3,13 @@ - + slack_bolt.authorization.authorize API documentation - + @@ -74,7 +74,7 @@

          Subclasses

          class CallableAuthorize -(*, logger: logging.Logger, func: Callable[..., AuthorizeResult]) +(*,
          logger: logging.Logger,
          func: Callable[..., AuthorizeResult])

          When you pass the authorize argument in AsyncApp constructor, @@ -161,7 +161,7 @@

          Ancestors

          class InstallationStoreAuthorize -(*, logger: logging.Logger, installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore, client_id: Optional[str] = None, client_secret: Optional[str] = None, token_rotation_expiration_minutes: Optional[int] = None, bot_only: bool = False, cache_enabled: bool = False, client: Optional[slack_sdk.web.client.WebClient] = None, user_token_resolution: str = 'authed_user') +(*,
          logger: logging.Logger,
          installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore,
          client_id: str | None = None,
          client_secret: str | None = None,
          token_rotation_expiration_minutes: int | None = None,
          bot_only: bool = False,
          cache_enabled: bool = False,
          client: slack_sdk.web.client.WebClient | None = None,
          user_token_resolution: str = 'authed_user')

          If you use the OAuth flow settings, this authorize implementation will be used. @@ -457,7 +457,7 @@

          Class variables

          -
          var token_rotator : Optional[slack_sdk.oauth.token_rotation.rotator.TokenRotator]
          +
          var token_rotator : slack_sdk.oauth.token_rotation.rotator.TokenRotator | None
          @@ -505,7 +505,7 @@

          -

          Generated by pdoc 0.11.1.

          +

          Generated by pdoc 0.11.3.

          diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize_args.html b/docs/static/api-docs/slack_bolt/authorization/authorize_args.html index fec8531bf..5e9e210b2 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize_args.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize_args.html @@ -3,13 +3,13 @@ - + slack_bolt.authorization.authorize_args API documentation - + @@ -37,7 +37,7 @@

          Classes

          class AuthorizeArgs -(*, context: BoltContext, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str]) +(*,
          context: BoltContext,
          enterprise_id: str | None,
          team_id: str | None,
          user_id: str | None)

          The full list of the arguments passed to authorize function.

          @@ -97,7 +97,7 @@

          Class variables

          -
          var enterprise_id : Optional[str]
          +
          var enterprise_id : str | None
          @@ -105,11 +105,11 @@

          Class variables

          -
          var team_id : Optional[str]
          +
          var team_id : str | None
          -
          var user_id : Optional[str]
          +
          var user_id : str | None
          @@ -147,7 +147,7 @@

          diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize_result.html b/docs/static/api-docs/slack_bolt/authorization/authorize_result.html index 3c5ae265f..9202bd546 100644 --- a/docs/static/api-docs/slack_bolt/authorization/authorize_result.html +++ b/docs/static/api-docs/slack_bolt/authorization/authorize_result.html @@ -3,13 +3,13 @@ - + slack_bolt.authorization.authorize_result API documentation - + @@ -37,7 +37,7 @@

          Classes

          class AuthorizeResult -(*, enterprise_id: Optional[str], team_id: Optional[str], team: Optional[str] = None, url: Optional[str] = None, bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None) +(*,
          enterprise_id: str | None,
          team_id: str | None,
          team: str | None = None,
          url: str | None = None,
          bot_user_id: str | None = None,
          bot_id: str | None = None,
          bot_token: str | None = None,
          bot_scopes: str | Sequence[str] | None = None,
          user_id: str | None = None,
          user: str | None = None,
          user_token: str | None = None,
          user_scopes: str | Sequence[str] | None = None)

          Authorize function call result

          @@ -183,51 +183,51 @@

          Ancestors

        Class variables

        -
        var bot_id : Optional[str]
        +
        var bot_id : str | None
        -
        var bot_scopes : Optional[Sequence[str]]
        +
        var bot_scopes : Sequence[str] | None
        -
        var bot_token : Optional[str]
        +
        var bot_token : str | None
        -
        var bot_user_id : Optional[str]
        +
        var bot_user_id : str | None
        -
        var enterprise_id : Optional[str]
        +
        var enterprise_id : str | None
        -
        var team : Optional[str]
        +
        var team : str | None
        -
        var team_id : Optional[str]
        +
        var team_id : str | None
        -
        var url : Optional[str]
        +
        var url : str | None
        -
        var user : Optional[str]
        +
        var user : str | None
        -
        var user_id : Optional[str]
        +
        var user_id : str | None
        -
        var user_scopes : Optional[Sequence[str]]
        +
        var user_scopes : Sequence[str] | None
        -
        var user_token : Optional[str]
        +
        var user_token : str | None
        @@ -235,7 +235,7 @@

        Class variables

        Static methods

        -def from_auth_test_response(*, bot_token: Optional[str] = None, user_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse')], user_auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse'), ForwardRef(None)] = None) +def from_auth_test_response(*,
        bot_token: str | None = None,
        user_token: str | None = None,
        bot_scopes: str | Sequence[str] | None = None,
        user_scopes: str | Sequence[str] | None = None,
        auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
        user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
        @@ -281,7 +281,7 @@

        diff --git a/docs/static/api-docs/slack_bolt/authorization/index.html b/docs/static/api-docs/slack_bolt/authorization/index.html index eafddd773..5b53ddb36 100644 --- a/docs/static/api-docs/slack_bolt/authorization/index.html +++ b/docs/static/api-docs/slack_bolt/authorization/index.html @@ -3,14 +3,14 @@ - + slack_bolt.authorization API documentation - + @@ -64,7 +64,7 @@

        Classes

        class AuthorizeResult -(*, enterprise_id: Optional[str], team_id: Optional[str], team: Optional[str] = None, url: Optional[str] = None, bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None) +(*,
        enterprise_id: str | None,
        team_id: str | None,
        team: str | None = None,
        url: str | None = None,
        bot_user_id: str | None = None,
        bot_id: str | None = None,
        bot_token: str | None = None,
        bot_scopes: str | Sequence[str] | None = None,
        user_id: str | None = None,
        user: str | None = None,
        user_token: str | None = None,
        user_scopes: str | Sequence[str] | None = None)

        Authorize function call result

        @@ -210,51 +210,51 @@

        Ancestors

      Class variables

      -
      var bot_id : Optional[str]
      +
      var bot_id : str | None
      -
      var bot_scopes : Optional[Sequence[str]]
      +
      var bot_scopes : Sequence[str] | None
      -
      var bot_token : Optional[str]
      +
      var bot_token : str | None
      -
      var bot_user_id : Optional[str]
      +
      var bot_user_id : str | None
      -
      var enterprise_id : Optional[str]
      +
      var enterprise_id : str | None
      -
      var team : Optional[str]
      +
      var team : str | None
      -
      var team_id : Optional[str]
      +
      var team_id : str | None
      -
      var url : Optional[str]
      +
      var url : str | None
      -
      var user : Optional[str]
      +
      var user : str | None
      -
      var user_id : Optional[str]
      +
      var user_id : str | None
      -
      var user_scopes : Optional[Sequence[str]]
      +
      var user_scopes : Sequence[str] | None
      -
      var user_token : Optional[str]
      +
      var user_token : str | None
      @@ -262,7 +262,7 @@

      Class variables

      Static methods

      -def from_auth_test_response(*, bot_token: Optional[str] = None, user_token: Optional[str] = None, bot_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, user_scopes: Union[str, Sequence[str], ForwardRef(None)] = None, auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse')], user_auth_test_response: Union[slack_sdk.web.slack_response.SlackResponse, ForwardRef('AsyncSlackResponse'), ForwardRef(None)] = None) +def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
      @@ -317,7 +317,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/ack/ack.html b/docs/static/api-docs/slack_bolt/context/ack/ack.html index 14283a40c..b5e5c898f 100644 --- a/docs/static/api-docs/slack_bolt/context/ack/ack.html +++ b/docs/static/api-docs/slack_bolt/context/ack/ack.html @@ -3,13 +3,13 @@ - + slack_bolt.context.ack.ack API documentation - + @@ -83,7 +83,7 @@

      Classes

      Class variables

      -
      var response : Optional[BoltResponse]
      +
      var responseBoltResponse | None
      @@ -116,7 +116,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/ack/async_ack.html b/docs/static/api-docs/slack_bolt/context/ack/async_ack.html index ce0041c07..0eb7e8043 100644 --- a/docs/static/api-docs/slack_bolt/context/ack/async_ack.html +++ b/docs/static/api-docs/slack_bolt/context/ack/async_ack.html @@ -3,13 +3,13 @@ - + slack_bolt.context.ack.async_ack API documentation - + @@ -83,7 +83,7 @@

      Classes

      Class variables

      -
      var response : Optional[BoltResponse]
      +
      var responseBoltResponse | None
      @@ -116,7 +116,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/ack/index.html b/docs/static/api-docs/slack_bolt/context/ack/index.html index 674dd9232..cc179b158 100644 --- a/docs/static/api-docs/slack_bolt/context/ack/index.html +++ b/docs/static/api-docs/slack_bolt/context/ack/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.ack API documentation - + @@ -98,7 +98,7 @@

      Classes

      Class variables

      -
      var response : Optional[BoltResponse]
      +
      var responseBoltResponse | None
      @@ -138,7 +138,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/ack/internals.html b/docs/static/api-docs/slack_bolt/context/ack/internals.html index 33ece9a0d..b1b1a2538 100644 --- a/docs/static/api-docs/slack_bolt/context/ack/internals.html +++ b/docs/static/api-docs/slack_bolt/context/ack/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.context.ack.internals API documentation - + @@ -49,7 +49,7 @@

      Module slack_bolt.context.ack.internals

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html index fcdc21ca4..16f084624 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.assistant_utilities API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AssistantUtilities -(*, payload: dict, context: BoltContext, thread_context_store: Optional[AssistantThreadContextStore] = None) +(*,
      payload: dict,
      context: BoltContext,
      thread_context_store: AssistantThreadContextStore | None = None)
      @@ -271,7 +271,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html b/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html index 8bbbfd414..22468df4e 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.async_assistant_utilities API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncAssistantUtilities -(*, payload: dict, context: AsyncBoltContext, thread_context_store: Optional[AsyncAssistantThreadContextStore] = None) +(*,
      payload: dict,
      context: AsyncBoltContext,
      thread_context_store: AsyncAssistantThreadContextStore | None = None)
      @@ -265,7 +265,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/index.html b/docs/static/api-docs/slack_bolt/context/assistant/index.html index 6ff8a0a1a..d2026e015 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/index.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant API documentation - + @@ -81,7 +81,7 @@

      Sub-modules

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/internals.html b/docs/static/api-docs/slack_bolt/context/assistant/internals.html index 6e576c1f5..f2124d9ff 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/internals.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.internals API documentation - + @@ -64,7 +64,7 @@

      Functions

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html index 59121d712..755d63b57 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.thread_context API documentation - + @@ -76,11 +76,11 @@

      Class variables

      -
      var enterprise_id : Optional[str]
      +
      var enterprise_id : str | None
      -
      var team_id : Optional[str]
      +
      var team_id : str | None
      @@ -115,7 +115,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html index e7d54ace9..a9e928c7c 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.thread_context_store.async_store API documentation - + @@ -58,7 +58,7 @@

      Subclasses

      Methods

      -async def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +async def find(self, *, channel_id: str, thread_ts: str) ‑> AssistantThreadContext | None
      @@ -99,7 +99,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html index d0bd8d9cd..d2696dc21 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.thread_context_store.default_async_store API documentation - + @@ -107,7 +107,7 @@

      Class variables

      Methods

      -async def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +async def find(self, *, channel_id: str, thread_ts: str) ‑> AssistantThreadContext | None
      @@ -150,7 +150,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html index 7d53db27b..c20a3413f 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.thread_context_store.default_store API documentation - + @@ -105,7 +105,7 @@

      Class variables

      Methods

      -def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +def find(self, *, channel_id: str, thread_ts: str) ‑> AssistantThreadContext | None
      @@ -148,7 +148,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html index d4ee18a6a..3a31ee12d 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.thread_context_store.file API documentation - + @@ -83,7 +83,7 @@

      Ancestors

      Methods

      -def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +def find(self, *, channel_id: str, thread_ts: str) ‑> AssistantThreadContext | None
      @@ -124,7 +124,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html index 19e88534b..dc3024804 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.thread_context_store API documentation - + @@ -81,7 +81,7 @@

      Sub-modules

      diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html index 247791024..77c9e1b2b 100644 --- a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html +++ b/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html @@ -3,13 +3,13 @@ - + slack_bolt.context.assistant.thread_context_store.store API documentation - + @@ -59,7 +59,7 @@

      Subclasses

      Methods

      -def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +def find(self, *, channel_id: str, thread_ts: str) ‑> AssistantThreadContext | None
      @@ -100,7 +100,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/async_context.html b/docs/static/api-docs/slack_bolt/context/async_context.html index 146d13b98..af07618aa 100644 --- a/docs/static/api-docs/slack_bolt/context/async_context.html +++ b/docs/static/api-docs/slack_bolt/context/async_context.html @@ -3,13 +3,13 @@ - + slack_bolt.context.async_context API documentation - + @@ -427,7 +427,7 @@

      Returns

      return self["fail"]

      -
      prop get_thread_context : Optional[AsyncGetThreadContext]
      +
      prop get_thread_contextAsyncGetThreadContext | None
      @@ -452,7 +452,7 @@

      Returns

      return self["listener_runner"]
      -
      prop respond : Optional[AsyncRespond]
      +
      prop respondAsyncRespond | None

      respond() function for this request.

      @app.action("button")
      @@ -499,7 +499,7 @@ 

      Returns

      return self["respond"]
      -
      prop save_thread_context : Optional[AsyncSaveThreadContext]
      +
      prop save_thread_contextAsyncSaveThreadContext | None
      @@ -554,7 +554,7 @@

      Returns

      return self["say"]
      -
      prop set_status : Optional[AsyncSetStatus]
      +
      prop set_statusAsyncSetStatus | None
      @@ -566,7 +566,7 @@

      Returns

      return self.get("set_status")
      -
      prop set_suggested_prompts : Optional[AsyncSetSuggestedPrompts]
      +
      prop set_suggested_promptsAsyncSetSuggestedPrompts | None
      @@ -578,7 +578,7 @@

      Returns

      return self.get("set_suggested_prompts")
      -
      prop set_title : Optional[AsyncSetTitle]
      +
      prop set_titleAsyncSetTitle | None
      @@ -668,7 +668,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/base_context.html b/docs/static/api-docs/slack_bolt/context/base_context.html index 587a5efd5..e0299dc93 100644 --- a/docs/static/api-docs/slack_bolt/context/base_context.html +++ b/docs/static/api-docs/slack_bolt/context/base_context.html @@ -3,13 +3,13 @@ - + slack_bolt.context.base_context API documentation - + @@ -253,7 +253,7 @@

      Class variables

      Instance variables

      -
      prop actor_enterprise_id : Optional[str]
      +
      prop actor_enterprise_id : str | None

      The action's actor's Enterprise Grid organization ID. Note that this property is especially useful for handling events in Slack Connect channels. @@ -271,7 +271,7 @@

      Instance variables

      return self.get("actor_enterprise_id")
      -
      prop actor_team_id : Optional[str]
      +
      prop actor_team_id : str | None

      The action's actor's workspace ID. Note that this property is especially useful for handling events in Slack Connect channels. @@ -289,7 +289,7 @@

      Instance variables

      return self.get("actor_team_id")
      -
      prop actor_user_id : Optional[str]
      +
      prop actor_user_id : str | None

      The action's actor's user ID. Note that this property is especially useful for handling events in Slack Connect channels. @@ -307,7 +307,7 @@

      Instance variables

      return self.get("actor_user_id")
      -
      prop authorize_result : Optional[AuthorizeResult]
      +
      prop authorize_resultAuthorizeResult | None

      The authorize result resolved for this request.

      @@ -320,7 +320,7 @@

      Instance variables

      return self.get("authorize_result")
      -
      prop bot_id : Optional[str]
      +
      prop bot_id : str | None

      The bot ID resolved for this request.

      @@ -333,7 +333,7 @@

      Instance variables

      return self.get("bot_id")
      -
      prop bot_token : Optional[str]
      +
      prop bot_token : str | None

      The bot token resolved for this request.

      @@ -346,7 +346,7 @@

      Instance variables

      return self.get("bot_token")
      -
      prop bot_user_id : Optional[str]
      +
      prop bot_user_id : str | None

      The bot user ID resolved for this request.

      @@ -359,7 +359,7 @@

      Instance variables

      return self.get("bot_user_id")
      -
      prop channel_id : Optional[str]
      +
      prop channel_id : str | None

      The conversation ID associated with this request.

      @@ -372,7 +372,7 @@

      Instance variables

      return self.get("channel_id")
      -
      prop enterprise_id : Optional[str]
      +
      prop enterprise_id : str | None

      The Enterprise Grid Organization ID of this request.

      @@ -385,7 +385,7 @@

      Instance variables

      return self.get("enterprise_id")
      -
      prop function_bot_access_token : Optional[str]
      +
      prop function_bot_access_token : str | None

      The bot token resolved for this function request. Only available for function_executed and interactivity events scoped to a custom step.

      @@ -401,7 +401,7 @@

      Instance variables

      return self.get("function_bot_access_token")
      -
      prop function_execution_id : Optional[str]
      +
      prop function_execution_id : str | None

      The function_execution_id associated with this request. Only available for function_executed and interactivity events scoped to a custom step.

      @@ -417,7 +417,7 @@

      Instance variables

      return self.get("function_execution_id")
      -
      prop inputs : Optional[Dict[str, Any]]
      +
      prop inputs : Dict[str, Any] | None

      The inputs associated with this request. Only available for function_executed and interactivity events scoped to a custom step.

      @@ -433,7 +433,7 @@

      Instance variables

      return self.get("inputs")
      -
      prop is_enterprise_install : Optional[bool]
      +
      prop is_enterprise_install : bool | None

      True if the request is associated with an Org-wide installation.

      @@ -459,7 +459,7 @@

      Instance variables

      return self["logger"]
      -
      prop matches : Optional[Tuple]
      +
      prop matches : Tuple | None

      Returns all the matched parts in message listener's regexp

      @@ -472,7 +472,7 @@

      Instance variables

      return self.get("matches")
      -
      prop response_url : Optional[str]
      +
      prop response_url : str | None

      The response_url associated with this request.

      @@ -485,7 +485,7 @@

      Instance variables

      return self.get("response_url")
      -
      prop team_id : Optional[str]
      +
      prop team_id : str | None

      The Workspace ID of this request.

      @@ -498,7 +498,7 @@

      Instance variables

      return self.get("team_id")
      -
      prop thread_ts : Optional[str]
      +
      prop thread_ts : str | None

      The conversation thread's ID associated with this request.

      @@ -511,7 +511,7 @@

      Instance variables

      return self.get("thread_ts")
      -
      prop token : Optional[str]
      +
      prop token : str | None

      The (bot/user) token resolved for this request.

      @@ -524,7 +524,7 @@

      Instance variables

      return self.get("token")
      -
      prop user_id : Optional[str]
      +
      prop user_id : str | None

      The user ID associated ith this request.

      @@ -537,7 +537,7 @@

      Instance variables

      return self.get("user_id")
      -
      prop user_token : Optional[str]
      +
      prop user_token : str | None

      The user token resolved for this request.

      @@ -554,7 +554,7 @@

      Instance variables

      Methods

      -def set_authorize_result(self, authorize_result: AuthorizeResult) +def set_authorize_result(self,
      authorize_result: AuthorizeResult)
      @@ -612,7 +612,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/complete/async_complete.html b/docs/static/api-docs/slack_bolt/context/complete/async_complete.html index 64af656bb..36fc95c9c 100644 --- a/docs/static/api-docs/slack_bolt/context/complete/async_complete.html +++ b/docs/static/api-docs/slack_bolt/context/complete/async_complete.html @@ -3,13 +3,13 @@ - + slack_bolt.context.complete.async_complete API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncComplete -(client: slack_sdk.web.async_client.AsyncWebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.async_client.AsyncWebClient,
      function_execution_id: str | None)
      @@ -82,7 +82,7 @@

      Class variables

      -
      var function_execution_id : Optional[str]
      +
      var function_execution_id : str | None
      @@ -116,7 +116,7 @@

      diff --git a/docs/static/api-docs/slack_bolt/context/complete/complete.html b/docs/static/api-docs/slack_bolt/context/complete/complete.html index 50e0fe920..7e317bf5f 100644 --- a/docs/static/api-docs/slack_bolt/context/complete/complete.html +++ b/docs/static/api-docs/slack_bolt/context/complete/complete.html @@ -3,13 +3,13 @@ - + slack_bolt.context.complete.complete API documentation - + @@ -37,7 +37,7 @@

      Classes

      class Complete -(client: slack_sdk.web.client.WebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.client.WebClient, function_execution_id: str | None)
      @@ -80,7 +80,7 @@

      Class variables

      -
      var function_execution_id : Optional[str]
      +
      var function_execution_id : str | None
      @@ -114,7 +114,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/complete/index.html b/docs/static/api-docs/slack_bolt/context/complete/index.html index a35d1b37e..d4b854f00 100644 --- a/docs/static/api-docs/slack_bolt/context/complete/index.html +++ b/docs/static/api-docs/slack_bolt/context/complete/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.complete API documentation - + @@ -48,7 +48,7 @@

      Classes

      class Complete -(client: slack_sdk.web.client.WebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.client.WebClient, function_execution_id: str | None)
      @@ -91,7 +91,7 @@

      Class variables

      -
      var function_execution_id : Optional[str]
      +
      var function_execution_id : str | None
      @@ -131,7 +131,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/context.html b/docs/static/api-docs/slack_bolt/context/context.html index 04a9af74d..a4b72f664 100644 --- a/docs/static/api-docs/slack_bolt/context/context.html +++ b/docs/static/api-docs/slack_bolt/context/context.html @@ -3,13 +3,13 @@ - + slack_bolt.context.context API documentation - + @@ -428,7 +428,7 @@

      Returns

      return self["fail"]

      -
      prop get_thread_context : Optional[GetThreadContext]
      +
      prop get_thread_contextGetThreadContext | None
      @@ -453,7 +453,7 @@

      Returns

      return self["listener_runner"]
      -
      prop respond : Optional[Respond]
      +
      prop respondRespond | None

      respond() function for this request.

      @app.action("button")
      @@ -500,7 +500,7 @@ 

      Returns

      return self["respond"]
      -
      prop save_thread_context : Optional[SaveThreadContext]
      +
      prop save_thread_contextSaveThreadContext | None
      @@ -555,7 +555,7 @@

      Returns

      return self["say"]
      -
      prop set_status : Optional[SetStatus]
      +
      prop set_statusSetStatus | None
      @@ -567,7 +567,7 @@

      Returns

      return self.get("set_status")
      -
      prop set_suggested_prompts : Optional[SetSuggestedPrompts]
      +
      prop set_suggested_promptsSetSuggestedPrompts | None
      @@ -579,7 +579,7 @@

      Returns

      return self.get("set_suggested_prompts")
      -
      prop set_title : Optional[SetTitle]
      +
      prop set_titleSetTitle | None
      @@ -669,7 +669,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/fail/async_fail.html b/docs/static/api-docs/slack_bolt/context/fail/async_fail.html index e27c15281..49528b4cd 100644 --- a/docs/static/api-docs/slack_bolt/context/fail/async_fail.html +++ b/docs/static/api-docs/slack_bolt/context/fail/async_fail.html @@ -3,13 +3,13 @@ - + slack_bolt.context.fail.async_fail API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncFail -(client: slack_sdk.web.async_client.AsyncWebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.async_client.AsyncWebClient,
      function_execution_id: str | None)
      @@ -80,7 +80,7 @@

      Class variables

      -
      var function_execution_id : Optional[str]
      +
      var function_execution_id : str | None
      @@ -114,7 +114,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/fail/fail.html b/docs/static/api-docs/slack_bolt/context/fail/fail.html index f33b4d498..6f191fd74 100644 --- a/docs/static/api-docs/slack_bolt/context/fail/fail.html +++ b/docs/static/api-docs/slack_bolt/context/fail/fail.html @@ -3,13 +3,13 @@ - + slack_bolt.context.fail.fail API documentation - + @@ -37,7 +37,7 @@

      Classes

      class Fail -(client: slack_sdk.web.client.WebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.client.WebClient, function_execution_id: str | None)
      @@ -80,7 +80,7 @@

      Class variables

      -
      var function_execution_id : Optional[str]
      +
      var function_execution_id : str | None
      @@ -114,7 +114,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/fail/index.html b/docs/static/api-docs/slack_bolt/context/fail/index.html index eecb86147..a4939084f 100644 --- a/docs/static/api-docs/slack_bolt/context/fail/index.html +++ b/docs/static/api-docs/slack_bolt/context/fail/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.fail API documentation - + @@ -48,7 +48,7 @@

      Classes

      class Fail -(client: slack_sdk.web.client.WebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.client.WebClient, function_execution_id: str | None)
      @@ -91,7 +91,7 @@

      Class variables

      -
      var function_execution_id : Optional[str]
      +
      var function_execution_id : str | None
      @@ -131,7 +131,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html b/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html index 5db376357..58e473fc2 100644 --- a/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html +++ b/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html @@ -3,13 +3,13 @@ - + slack_bolt.context.get_thread_context.async_get_thread_context API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncGetThreadContext -(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +(thread_context_store: AsyncAssistantThreadContextStore,
      channel_id: str,
      thread_ts: str,
      payload: dict)
      @@ -143,7 +143,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html b/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html index 887cc1525..24f38365a 100644 --- a/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html +++ b/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html @@ -3,13 +3,13 @@ - + slack_bolt.context.get_thread_context.get_thread_context API documentation - + @@ -37,7 +37,7 @@

      Classes

      class GetThreadContext -(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +(thread_context_store: AssistantThreadContextStore,
      channel_id: str,
      thread_ts: str,
      payload: dict)
      @@ -143,7 +143,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html b/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html index 71d68842d..3cffdd928 100644 --- a/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html +++ b/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.get_thread_context API documentation - + @@ -48,7 +48,7 @@

      Classes

      class GetThreadContext -(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict) +(thread_context_store: AssistantThreadContextStore,
      channel_id: str,
      thread_ts: str,
      payload: dict)
      @@ -160,7 +160,7 @@

      diff --git a/docs/static/api-docs/slack_bolt/context/index.html b/docs/static/api-docs/slack_bolt/context/index.html index b25e85281..293e1f0ff 100644 --- a/docs/static/api-docs/slack_bolt/context/index.html +++ b/docs/static/api-docs/slack_bolt/context/index.html @@ -3,14 +3,14 @@ - + slack_bolt.context API documentation - + @@ -492,7 +492,7 @@

      Returns

      return self["fail"]

      -
      prop get_thread_context : Optional[GetThreadContext]
      +
      prop get_thread_contextGetThreadContext | None
      @@ -517,7 +517,7 @@

      Returns

      return self["listener_runner"]
      -
      prop respond : Optional[Respond]
      +
      prop respondRespond | None

      slack_bolt.context.respond function for this request.

      @app.action("button")
      @@ -564,7 +564,7 @@ 

      Returns

      return self["respond"]
      -
      prop save_thread_context : Optional[SaveThreadContext]
      +
      prop save_thread_contextSaveThreadContext | None
      @@ -619,7 +619,7 @@

      Returns

      return self["say"]
      -
      prop set_status : Optional[SetStatus]
      +
      prop set_statusSetStatus | None
      @@ -631,7 +631,7 @@

      Returns

      return self.get("set_status")
      -
      prop set_suggested_prompts : Optional[SetSuggestedPrompts]
      +
      prop set_suggested_promptsSetSuggestedPrompts | None
      @@ -643,7 +643,7 @@

      Returns

      return self.get("set_suggested_prompts")
      -
      prop set_title : Optional[SetTitle]
      +
      prop set_titleSetTitle | None
      @@ -751,7 +751,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/respond/async_respond.html b/docs/static/api-docs/slack_bolt/context/respond/async_respond.html index 56a426ba8..2295005f8 100644 --- a/docs/static/api-docs/slack_bolt/context/respond/async_respond.html +++ b/docs/static/api-docs/slack_bolt/context/respond/async_respond.html @@ -3,13 +3,13 @@ - + slack_bolt.context.respond.async_respond API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncRespond -(*, response_url: Optional[str], proxy: Optional[str] = None, ssl: Optional[ssl.SSLContext] = None) +(*,
      response_url: str | None,
      proxy: str | None = None,
      ssl: ssl.SSLContext | None = None)
      @@ -106,15 +106,15 @@

      Classes

      Class variables

      -
      var proxy : Optional[str]
      +
      var proxy : str | None
      -
      var response_url : Optional[str]
      +
      var response_url : str | None
      -
      var ssl : Optional[ssl.SSLContext]
      +
      var ssl : ssl.SSLContext | None
      @@ -149,7 +149,7 @@

      diff --git a/docs/static/api-docs/slack_bolt/context/respond/index.html b/docs/static/api-docs/slack_bolt/context/respond/index.html index 1071967ce..3b46a90d3 100644 --- a/docs/static/api-docs/slack_bolt/context/respond/index.html +++ b/docs/static/api-docs/slack_bolt/context/respond/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.respond API documentation - + @@ -52,7 +52,7 @@

      Classes

      class Respond -(*, response_url: Optional[str], proxy: Optional[str] = None, ssl: Optional[ssl.SSLContext] = None) +(*,
      response_url: str | None,
      proxy: str | None = None,
      ssl: ssl.SSLContext | None = None)
      @@ -121,15 +121,15 @@

      Classes

      Class variables

      -
      var proxy : Optional[str]
      +
      var proxy : str | None
      -
      var response_url : Optional[str]
      +
      var response_url : str | None
      -
      var ssl : Optional[ssl.SSLContext]
      +
      var ssl : ssl.SSLContext | None
      @@ -171,7 +171,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/respond/internals.html b/docs/static/api-docs/slack_bolt/context/respond/internals.html index 79bc88f48..982d41ce9 100644 --- a/docs/static/api-docs/slack_bolt/context/respond/internals.html +++ b/docs/static/api-docs/slack_bolt/context/respond/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.context.respond.internals API documentation - + @@ -49,7 +49,7 @@

      Module slack_bolt.context.respond.internals

      diff --git a/docs/static/api-docs/slack_bolt/context/respond/respond.html b/docs/static/api-docs/slack_bolt/context/respond/respond.html index 8bc08afff..e22a7e9f6 100644 --- a/docs/static/api-docs/slack_bolt/context/respond/respond.html +++ b/docs/static/api-docs/slack_bolt/context/respond/respond.html @@ -3,13 +3,13 @@ - + slack_bolt.context.respond.respond API documentation - + @@ -37,7 +37,7 @@

      Classes

      class Respond -(*, response_url: Optional[str], proxy: Optional[str] = None, ssl: Optional[ssl.SSLContext] = None) +(*,
      response_url: str | None,
      proxy: str | None = None,
      ssl: ssl.SSLContext | None = None)
      @@ -106,15 +106,15 @@

      Classes

      Class variables

      -
      var proxy : Optional[str]
      +
      var proxy : str | None
      -
      var response_url : Optional[str]
      +
      var response_url : str | None
      -
      var ssl : Optional[ssl.SSLContext]
      +
      var ssl : ssl.SSLContext | None
      @@ -149,7 +149,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html b/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html index d1e5af19c..fe62dda13 100644 --- a/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html +++ b/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html @@ -3,13 +3,13 @@ - + slack_bolt.context.save_thread_context.async_save_thread_context API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncSaveThreadContext -(thread_context_store: AsyncAssistantThreadContextStore, channel_id: str, thread_ts: str) +(thread_context_store: AsyncAssistantThreadContextStore,
      channel_id: str,
      thread_ts: str)
      @@ -112,7 +112,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html b/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html index 0b593b7c2..548077179 100644 --- a/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html +++ b/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.save_thread_context API documentation - + @@ -48,7 +48,7 @@

      Classes

      class SaveThreadContext -(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str) +(thread_context_store: AssistantThreadContextStore,
      channel_id: str,
      thread_ts: str)
      @@ -129,7 +129,7 @@

      diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html b/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html index 6a693a49e..25d589fbc 100644 --- a/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html +++ b/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html @@ -3,13 +3,13 @@ - + slack_bolt.context.save_thread_context.save_thread_context API documentation - + @@ -37,7 +37,7 @@

      Classes

      class SaveThreadContext -(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str) +(thread_context_store: AssistantThreadContextStore,
      channel_id: str,
      thread_ts: str)
      @@ -112,7 +112,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/say/async_say.html b/docs/static/api-docs/slack_bolt/context/say/async_say.html index d4ca5ca65..4a05abb8b 100644 --- a/docs/static/api-docs/slack_bolt/context/say/async_say.html +++ b/docs/static/api-docs/slack_bolt/context/say/async_say.html @@ -3,13 +3,13 @@ - + slack_bolt.context.say.async_say API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncSay -(client: Optional[slack_sdk.web.async_client.AsyncWebClient], channel: Optional[str], thread_ts: Optional[str] = None, build_metadata: Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]] = None) +(client: slack_sdk.web.async_client.AsyncWebClient | None,
      channel: str | None,
      thread_ts: str | None = None,
      build_metadata: Callable[[], Awaitable[Dict | slack_sdk.models.metadata.Metadata]] | None = None)
      @@ -124,19 +124,19 @@

      Classes

      Class variables

      -
      var build_metadata : Optional[Callable[[], Awaitable[Union[Dict, slack_sdk.models.metadata.Metadata]]]]
      +
      var build_metadata : Callable[[], Awaitable[Dict | slack_sdk.models.metadata.Metadata]] | None
      -
      var channel : Optional[str]
      +
      var channel : str | None
      -
      var client : Optional[slack_sdk.web.async_client.AsyncWebClient]
      +
      var client : slack_sdk.web.async_client.AsyncWebClient | None
      -
      var thread_ts : Optional[str]
      +
      var thread_ts : str | None
      @@ -172,7 +172,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/say/index.html b/docs/static/api-docs/slack_bolt/context/say/index.html index c4bfed42f..5225d3a04 100644 --- a/docs/static/api-docs/slack_bolt/context/say/index.html +++ b/docs/static/api-docs/slack_bolt/context/say/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.say API documentation - + @@ -52,7 +52,7 @@

      Classes

      class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None, build_metadata: Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]] = None) +(client: slack_sdk.web.client.WebClient | None,
      channel: str | None,
      thread_ts: str | None = None,
      metadata: Dict | slack_sdk.models.metadata.Metadata | None = None,
      build_metadata: Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None = None)
      @@ -143,23 +143,23 @@

      Classes

      Class variables

      -
      var build_metadata : Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]]
      +
      var build_metadata : Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None
      -
      var channel : Optional[str]
      +
      var channel : str | None
      -
      var client : Optional[slack_sdk.web.client.WebClient]
      +
      var client : slack_sdk.web.client.WebClient | None
      -
      var metadata : Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]
      +
      var metadata : Dict | slack_sdk.models.metadata.Metadata | None
      -
      var thread_ts : Optional[str]
      +
      var thread_ts : str | None
      @@ -203,7 +203,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/say/internals.html b/docs/static/api-docs/slack_bolt/context/say/internals.html index 175c46afa..b807954f0 100644 --- a/docs/static/api-docs/slack_bolt/context/say/internals.html +++ b/docs/static/api-docs/slack_bolt/context/say/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.context.say.internals API documentation - + @@ -49,7 +49,7 @@

      Module slack_bolt.context.say.internals

      diff --git a/docs/static/api-docs/slack_bolt/context/say/say.html b/docs/static/api-docs/slack_bolt/context/say/say.html index db3bc1675..26f003197 100644 --- a/docs/static/api-docs/slack_bolt/context/say/say.html +++ b/docs/static/api-docs/slack_bolt/context/say/say.html @@ -3,13 +3,13 @@ - + slack_bolt.context.say.say API documentation - + @@ -37,7 +37,7 @@

      Classes

      class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None, build_metadata: Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]] = None) +(client: slack_sdk.web.client.WebClient | None,
      channel: str | None,
      thread_ts: str | None = None,
      metadata: Dict | slack_sdk.models.metadata.Metadata | None = None,
      build_metadata: Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None = None)
      @@ -128,23 +128,23 @@

      Classes

      Class variables

      -
      var build_metadata : Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]]
      +
      var build_metadata : Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None
      -
      var channel : Optional[str]
      +
      var channel : str | None
      -
      var client : Optional[slack_sdk.web.client.WebClient]
      +
      var client : slack_sdk.web.client.WebClient | None
      -
      var metadata : Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]
      +
      var metadata : Dict | slack_sdk.models.metadata.Metadata | None
      -
      var thread_ts : Optional[str]
      +
      var thread_ts : str | None
      @@ -181,7 +181,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html b/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html index 0a05750ae..d78b37f3e 100644 --- a/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html +++ b/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_status.async_set_status API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncSetStatus -(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.async_client.AsyncWebClient,
      channel_id: str,
      thread_ts: str)
      @@ -112,7 +112,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_status/index.html b/docs/static/api-docs/slack_bolt/context/set_status/index.html index 72e084e96..81d1b9242 100644 --- a/docs/static/api-docs/slack_bolt/context/set_status/index.html +++ b/docs/static/api-docs/slack_bolt/context/set_status/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_status API documentation - + @@ -129,7 +129,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_status/set_status.html b/docs/static/api-docs/slack_bolt/context/set_status/set_status.html index 869d61a2f..98dc060fd 100644 --- a/docs/static/api-docs/slack_bolt/context/set_status/set_status.html +++ b/docs/static/api-docs/slack_bolt/context/set_status/set_status.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_status.set_status API documentation - + @@ -112,7 +112,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html index a553aa59d..1b8ae3c7f 100644 --- a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html +++ b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncSetSuggestedPrompts -(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.async_client.AsyncWebClient,
      channel_id: str,
      thread_ts: str)
      @@ -60,7 +60,11 @@

      Classes

      self.channel_id = channel_id self.thread_ts = thread_ts - async def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> AsyncSlackResponse: + async def __call__( + self, + prompts: List[Union[str, Dict[str, str]]], + title: Optional[str] = None, + ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): @@ -72,6 +76,7 @@

      Classes

      channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, + title=title, )

      Class variables

      @@ -119,7 +124,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html index bc591bbad..612c3b74d 100644 --- a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html +++ b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_suggested_prompts API documentation - + @@ -71,7 +71,11 @@

      Classes

      self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse: + def __call__( + self, + prompts: List[Union[str, Dict[str, str]]], + title: Optional[str] = None, + ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): @@ -83,6 +87,7 @@

      Classes

      channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, + title=title, )

      Class variables

      @@ -136,7 +141,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html index 685518619..b106c588f 100644 --- a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html +++ b/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_suggested_prompts.set_suggested_prompts API documentation - + @@ -60,7 +60,11 @@

      Classes

      self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse: + def __call__( + self, + prompts: List[Union[str, Dict[str, str]]], + title: Optional[str] = None, + ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): @@ -72,6 +76,7 @@

      Classes

      channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, + title=title, )

      Class variables

      @@ -119,7 +124,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html b/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html index 388ab25ce..954edca46 100644 --- a/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html +++ b/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_title.async_set_title API documentation - + @@ -37,7 +37,7 @@

      Classes

      class AsyncSetTitle -(client: slack_sdk.web.async_client.AsyncWebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.async_client.AsyncWebClient,
      channel_id: str,
      thread_ts: str)
      @@ -112,7 +112,7 @@

      diff --git a/docs/static/api-docs/slack_bolt/context/set_title/index.html b/docs/static/api-docs/slack_bolt/context/set_title/index.html index 41192c77f..a27c74252 100644 --- a/docs/static/api-docs/slack_bolt/context/set_title/index.html +++ b/docs/static/api-docs/slack_bolt/context/set_title/index.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_title API documentation - + @@ -129,7 +129,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/context/set_title/set_title.html b/docs/static/api-docs/slack_bolt/context/set_title/set_title.html index 07faff2c3..c5f1e8e0d 100644 --- a/docs/static/api-docs/slack_bolt/context/set_title/set_title.html +++ b/docs/static/api-docs/slack_bolt/context/set_title/set_title.html @@ -3,13 +3,13 @@ - + slack_bolt.context.set_title.set_title API documentation - + @@ -112,7 +112,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/error/index.html b/docs/static/api-docs/slack_bolt/error/index.html index 2b3e8b043..196b8c43a 100644 --- a/docs/static/api-docs/slack_bolt/error/index.html +++ b/docs/static/api-docs/slack_bolt/error/index.html @@ -3,13 +3,13 @@ - + slack_bolt.error API documentation - + @@ -61,7 +61,7 @@

      Subclasses

      class BoltUnhandledRequestError -(*, request: Union[ForwardRef('BoltRequest'), ForwardRef('AsyncBoltRequest')], current_response: Optional[ForwardRef('BoltResponse')], last_global_middleware_name: Optional[str] = None) +(*,
      request: ForwardRef('BoltRequest') | ForwardRef('AsyncBoltRequest'),
      current_response: ForwardRef('BoltResponse') | None,
      last_global_middleware_name: str | None = None)

      General class in a Bolt app

      @@ -102,11 +102,11 @@

      Class variables

      -
      var current_response : Optional[BoltResponse]
      +
      var current_response : BoltResponse | None
      -
      var last_global_middleware_name : Optional[str]
      +
      var last_global_middleware_name : str | None
      @@ -149,7 +149,7 @@

      -

      Generated by pdoc 0.11.1.

      +

      Generated by pdoc 0.11.3.

      diff --git a/docs/static/api-docs/slack_bolt/index.html b/docs/static/api-docs/slack_bolt/index.html index 8ba57b21d..35b4600da 100644 --- a/docs/static/api-docs/slack_bolt/index.html +++ b/docs/static/api-docs/slack_bolt/index.html @@ -3,13 +3,13 @@ - + slack_bolt API documentation - + @@ -169,7 +169,7 @@

      Classes

      Class variables

      -
      var response : Optional[BoltResponse]
      +
      var responseBoltResponse | None
      @@ -177,7 +177,7 @@

      Class variables

      class App -(*, logger: Optional[logging.Logger] = None, name: Optional[str] = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: Optional[str] = None, token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[slack_sdk.web.client.WebClient] = None, before_authorize: Union[Middleware, Callable[..., Any], ForwardRef(None)] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore] = None, installation_store_bot_only: Optional[bool] = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, verification_token: Optional[str] = None, listener_executor: Optional[concurrent.futures._base.Executor] = None, assistant_thread_context_store: Optional[AssistantThreadContextStore] = None) +(*,
      logger: logging.Logger | None = None,
      name: str | None = None,
      process_before_response: bool = False,
      raise_error_for_unhandled_request: bool = False,
      signing_secret: str | None = None,
      token: str | None = None,
      token_verification_enabled: bool = True,
      client: slack_sdk.web.client.WebClient | None = None,
      before_authorize: Middleware | Callable[..., Any] | None = None,
      authorize: Callable[..., AuthorizeResult] | None = None,
      user_facing_authorize_error_message: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
      installation_store_bot_only: bool | None = None,
      request_verification_enabled: bool = True,
      ignoring_self_events_enabled: bool = True,
      ignoring_self_assistant_message_events_enabled: bool = True,
      ssl_check_enabled: bool = True,
      url_verification_enabled: bool = True,
      attaching_function_token_enabled: bool = True,
      oauth_settings: OAuthSettings | None = None,
      oauth_flow: OAuthFlow | None = None,
      verification_token: str | None = None,
      listener_executor: concurrent.futures._base.Executor | None = None,
      assistant_thread_context_store: AssistantThreadContextStore | None = None)

      Bolt App that provides functionalities to register middleware/listeners.

      @@ -1654,7 +1654,7 @@

      Instance variables

      return self._client
      -
      prop installation_store : Optional[slack_sdk.oauth.installation_store.installation_store.InstallationStore]
      +
      prop installation_store : slack_sdk.oauth.installation_store.installation_store.InstallationStore | None

      The slack_sdk.oauth.InstallationStore that can be used in the authorize middleware.

      @@ -1706,7 +1706,7 @@

      Instance variables

      return self._name
      -
      prop oauth_flow : Optional[OAuthFlow]
      +
      prop oauth_flowOAuthFlow | None

      Configured OAuthFlow object if exists.

      @@ -1735,7 +1735,7 @@

      Instance variables

      Methods

      -def action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def action(self,
      constraints: str | Pattern | Dict[str, str | Pattern],
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new action listener. This method can be used as either a decorator or a method.

      @@ -1766,33 +1766,33 @@

      Args

      -def assistant(self, assistant: Assistant) ‑> Optional[Callable] +def assistant(self,
      assistant: Assistant) ‑> Callable | None
      -def attachment_action(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def attachment_action(self,
      callback_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new interactive_message action listener. Refer to https://api.slack.com/legacy/message-buttons for details.

      -def block_action(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def block_action(self,
      constraints: str | Pattern | Dict[str, str | Pattern],
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new block_actions action listener. Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

      -def block_suggestion(self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def block_suggestion(self,
      action_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new block_suggestion listener.

      -def command(self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def command(self,
      command: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new slash command listener. @@ -1822,40 +1822,40 @@

      Args

      -def default_app_uninstalled_event_listener(self) ‑> Callable[..., Optional[BoltResponse]] +def default_app_uninstalled_event_listener(self) ‑> Callable[..., BoltResponse | None]
      -def default_tokens_revoked_event_listener(self) ‑> Callable[..., Optional[BoltResponse]] +def default_tokens_revoked_event_listener(self) ‑> Callable[..., BoltResponse | None]
      -def dialog_cancellation(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_cancellation(self,
      callback_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new dialog_cancellation listener. Refer to https://api.slack.com/dialogs for details.

      -def dialog_submission(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_submission(self,
      callback_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new dialog_submission listener. Refer to https://api.slack.com/dialogs for details.

      -def dialog_suggestion(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def dialog_suggestion(self,
      callback_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new dialog_suggestion listener. Refer to https://api.slack.com/dialogs for details.

      -def dispatch(self, req: BoltRequest) ‑> BoltResponse +def dispatch(self,
      req: BoltRequest) ‑> BoltResponse

      Applies all middleware and dispatches an incoming request from Slack to the right code path.

      @@ -1874,7 +1874,7 @@

      Returns

      -def error(self, func: Callable[..., Optional[BoltResponse]]) ‑> Callable[..., Optional[BoltResponse]] +def error(self,
      func: Callable[..., BoltResponse | None]) ‑> Callable[..., BoltResponse | None]

      Updates the global error handler. This method can be used as either a decorator or a method.

      @@ -1896,7 +1896,7 @@

      Args

      -def event(self, event: Union[str, Pattern, Dict[str, Union[str, Sequence[Union[str, Pattern, ForwardRef(None)]], ForwardRef(None)]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def event(self,
      event: str | Pattern | Dict[str, str | Sequence[str | Pattern | None] | None],
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new event listener. This method can be used as either a decorator or a method.

      @@ -1927,7 +1927,7 @@

      Args

      -def function(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def function(self,
      callback_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None,
      auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new Function listener. @@ -1960,13 +1960,13 @@

      Args

      -def global_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def global_shortcut(self,
      callback_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new global shortcut listener.

      -def message(self, keyword: Union[str, Pattern] = '', matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def message(self,
      keyword: str | Pattern = '',
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new message event listener. This method can be used as either a decorator or a method. @@ -1995,13 +1995,13 @@

      Args

      -def message_shortcut(self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def message_shortcut(self,
      callback_id: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new message shortcut listener.

      -def middleware(self, *args) ‑> Optional[Callable] +def middleware(self, *args) ‑> Callable | None

      Registers a new middleware to this app. @@ -2024,7 +2024,7 @@

      Args

      -def options(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def options(self,
      constraints: str | Pattern | Dict[str, str | Pattern],
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new options listener. @@ -2064,7 +2064,7 @@

      Args

      -def shortcut(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def shortcut(self,
      constraints: str | Pattern | Dict[str, str | Pattern],
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new shortcut listener. @@ -2100,7 +2100,7 @@

      Args

      -def start(self, port: int = 3000, path: str = '/slack/events', http_server_logger_enabled: bool = True) ‑> None +def start(self,
      port: int = 3000,
      path: str = '/slack/events',
      http_server_logger_enabled: bool = True) ‑> None

      Starts a web server for local development.

      @@ -2121,7 +2121,7 @@

      Args

      -def step(self, callback_id: Union[str, Pattern, WorkflowStepWorkflowStepBuilder], edit: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None, save: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None, execute: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable], ForwardRef(None)] = None) +def step(self,
      callback_id: str | Pattern | WorkflowStep | WorkflowStepBuilder,
      edit: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None,
      save: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None,
      execute: Callable[..., BoltResponse | None] | Listener | Sequence[Callable] | None = None)

      Deprecated

      @@ -2159,14 +2159,14 @@

      Args

      -def use(self, *args) ‑> Optional[Callable] +def use(self, *args) ‑> Callable | None

      Registers a new global middleware to this app. This method can be used as either a decorator or a method.

      Refer to App#middleware() method's docstring for details.

      -def view(self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view(self,
      constraints: str | Pattern | Dict[str, str | Pattern],
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new view_submission/view_closed event listener. @@ -2206,14 +2206,14 @@

      Args

      -def view_closed(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view_closed(self,
      constraints: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new view_closed listener. Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

      -def view_submission(self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None) ‑> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]] +def view_submission(self,
      constraints: str | Pattern,
      matchers: Sequence[Callable[..., bool]] | None = None,
      middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]

      Registers a new view_submission listener. @@ -2223,7 +2223,7 @@

      Args

      class Args -(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, next: Callable[[], None], **kwargs) +(*,
      logger: logging.Logger,
      client: slack_sdk.web.client.WebClient,
      req: BoltRequest,
      resp: BoltResponse,
      context: BoltContext,
      body: Dict[str, Any],
      payload: Dict[str, Any],
      options: Dict[str, Any] | None = None,
      shortcut: Dict[str, Any] | None = None,
      action: Dict[str, Any] | None = None,
      view: Dict[str, Any] | None = None,
      command: Dict[str, Any] | None = None,
      event: Dict[str, Any] | None = None,
      message: Dict[str, Any] | None = None,
      ack: Ack,
      say: Say,
      respond: Respond,
      complete: Complete,
      fail: Fail,
      set_status: SetStatus | None = None,
      set_title: SetTitle | None = None,
      set_suggested_prompts: SetSuggestedPrompts | None = None,
      get_thread_context: GetThreadContext | None = None,
      save_thread_context: SaveThreadContext | None = None,
      next: Callable[[], None],
      **kwargs)

      All the arguments in this class are available in any middleware / listeners. @@ -2415,7 +2415,7 @@

      Class variables

      ack() utility function, which returns acknowledgement to the Slack servers

      -
      var action : Optional[Dict[str, Any]]
      +
      var action : Dict[str, Any] | None

      An alias for payload in an @app.action listener

      @@ -2427,7 +2427,7 @@

      Class variables

      slack_sdk.web.WebClient instance with a valid token

      -
      var command : Optional[Dict[str, Any]]
      +
      var command : Dict[str, Any] | None

      An alias for payload in an @app.command listener

      @@ -2439,7 +2439,7 @@

      Class variables

      Context data associated with the incoming request

      -
      var event : Optional[Dict[str, Any]]
      +
      var event : Dict[str, Any] | None

      An alias for payload in an @app.event listener

      @@ -2447,7 +2447,7 @@

      Class variables

      fail() utility function, signal that the custom function failed to complete

      -
      var get_thread_context : Optional[GetThreadContext]
      +
      var get_thread_contextGetThreadContext | None

      get_thread_context() utility function for AI Agents & Assistants

      @@ -2455,7 +2455,7 @@

      Class variables

      Logger instance

      -
      var message : Optional[Dict[str, Any]]
      +
      var message : Dict[str, Any] | None

      An alias for payload in an @app.message listener

      @@ -2467,7 +2467,7 @@

      Class variables

      An alias of next() for avoiding the Python built-in method overrides in middleware functions

      -
      var options : Optional[Dict[str, Any]]
      +
      var options : Dict[str, Any] | None

      An alias for payload in an @app.options listener

      @@ -2495,7 +2495,7 @@

      Class variables

      Response representation

      -
      var save_thread_context : Optional[SaveThreadContext]
      +
      var save_thread_contextSaveThreadContext | None

      save_thread_context() utility function for AI Agents & Assistants

      @@ -2503,23 +2503,23 @@

      Class variables

      say() utility function, which calls chat.postMessage API with the associated channel ID

      -
      var set_status : Optional[SetStatus]
      +
      var set_statusSetStatus | None

      set_status() utility function for AI Agents & Assistants

      -
      var set_suggested_prompts : Optional[SetSuggestedPrompts]
      +
      var set_suggested_promptsSetSuggestedPrompts | None

      set_suggested_prompts() utility function for AI Agents & Assistants

      -
      var set_title : Optional[SetTitle]
      +
      var set_titleSetTitle | None

      set_title() utility function for AI Agents & Assistants

      -
      var shortcut : Optional[Dict[str, Any]]
      +
      var shortcut : Dict[str, Any] | None

      An alias for payload in an @app.shortcut listener

      -
      var view : Optional[Dict[str, Any]]
      +
      var view : Dict[str, Any] | None

      An alias for payload in an @app.view listener

      @@ -2527,7 +2527,7 @@

      Class variables

      class Assistant -(*, app_name: str = 'assistant', thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +(*,
      app_name: str = 'assistant',
      thread_context_store: AssistantThreadContextStore | None = None,
      logger: logging.Logger | None = None)

      A middleware can process request data before other middleware and listener functions.

      @@ -2805,11 +2805,11 @@

      Ancestors

    Class variables

    -
    var base_logger : Optional[logging.Logger]
    +
    var base_logger : logging.Logger | None
    -
    var thread_context_store : Optional[AssistantThreadContextStore]
    +
    var thread_context_storeAssistantThreadContextStore | None
    @@ -2817,7 +2817,7 @@

    Class variables

    Static methods

    -def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict) +def default_thread_context_changed(save_thread_context: SaveThreadContext,
    payload: dict)
    @@ -2826,31 +2826,31 @@

    Static methods

    Methods

    -def bot_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def bot_message(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def build_listener(self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> Listener +def build_listener(self,
    listener_or_functions: Listener | Callable | List[Callable],
    matchers: List[ListenerMatcher | Callable[..., bool]] | None = None,
    middleware: List[Middleware] | None = None,
    base_logger: logging.Logger | None = None) ‑> Listener
    -def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_context_changed(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def thread_started(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_started(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def user_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def user_message(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    @@ -2907,11 +2907,11 @@

    Class variables

    -
    var enterprise_id : Optional[str]
    +
    var enterprise_id : str | None
    -
    var team_id : Optional[str]
    +
    var team_id : str | None
    @@ -2941,7 +2941,7 @@

    Subclasses

    Methods

    -def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +def find(self, *, channel_id: str, thread_ts: str) ‑> AssistantThreadContext | None
    @@ -3347,7 +3347,7 @@

    Returns

    return self["fail"]
    -
    prop get_thread_context : Optional[GetThreadContext]
    +
    prop get_thread_contextGetThreadContext | None
    @@ -3372,7 +3372,7 @@

    Returns

    return self["listener_runner"]
    -
    prop respond : Optional[Respond]
    +
    prop respondRespond | None

    respond() function for this request.

    @app.action("button")
    @@ -3419,7 +3419,7 @@ 

    Returns

    return self["respond"]
    -
    prop save_thread_context : Optional[SaveThreadContext]
    +
    prop save_thread_contextSaveThreadContext | None
    @@ -3474,7 +3474,7 @@

    Returns

    return self["say"]
    -
    prop set_status : Optional[SetStatus]
    +
    prop set_statusSetStatus | None
    @@ -3486,7 +3486,7 @@

    Returns

    return self.get("set_status")
    -
    prop set_suggested_prompts : Optional[SetSuggestedPrompts]
    +
    prop set_suggested_promptsSetSuggestedPrompts | None
    @@ -3498,7 +3498,7 @@

    Returns

    return self.get("set_suggested_prompts")
    -
    prop set_title : Optional[SetTitle]
    +
    prop set_titleSetTitle | None
    @@ -3551,7 +3551,7 @@

    Inherited members

    class BoltRequest -(*, body: Union[str, dict], query: Union[str, Dict[str, str], Dict[str, Sequence[str]], ForwardRef(None)] = None, headers: Optional[Dict[str, Union[str, Sequence[str]]]] = None, context: Optional[Dict[str, Any]] = None, mode: str = 'http') +(*,
    body: str | dict,
    query: str | Dict[str, str] | Dict[str, Sequence[str]] | None = None,
    headers: Dict[str, str | Sequence[str]] | None = None,
    context: Dict[str, Any] | None = None,
    mode: str = 'http')

    Request to a Bolt app.

    @@ -3647,7 +3647,7 @@

    Class variables

    -
    var content_type : Optional[str]
    +
    var content_type : str | None
    @@ -3659,7 +3659,7 @@

    Class variables

    -
    var lazy_function_name : Optional[str]
    +
    var lazy_function_name : str | None
    @@ -3692,7 +3692,7 @@

    Methods

    class BoltResponse -(*, status: int, body: Union[str, dict] = '', headers: Optional[Dict[str, Union[str, Sequence[str]]]] = None) +(*,
    status: int,
    body: str | dict = '',
    headers: Dict[str, str | Sequence[str]] | None = None)

    The response from a Bolt app.

    @@ -3803,7 +3803,7 @@

    Methods

    class Complete -(client: slack_sdk.web.client.WebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.client.WebClient, function_execution_id: str | None)
    @@ -3846,7 +3846,7 @@

    Class variables

    -
    var function_execution_id : Optional[str]
    +
    var function_execution_id : str | None
    @@ -3854,7 +3854,7 @@

    Class variables

    class CustomListenerMatcher -(*, app_name: str, func: Callable[..., bool], base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    func: Callable[..., bool],
    base_logger: logging.Logger | None = None)
    @@ -3919,7 +3919,7 @@

    Inherited members

    class Fail -(client: slack_sdk.web.client.WebClient, function_execution_id: Optional[str]) +(client: slack_sdk.web.client.WebClient, function_execution_id: str | None)
    @@ -3962,7 +3962,7 @@

    Class variables

    -
    var function_execution_id : Optional[str]
    +
    var function_execution_id : str | None
    @@ -4016,7 +4016,7 @@

    Ancestors

    Methods

    -def find(self, *, channel_id: str, thread_ts: str) ‑> Optional[AssistantThreadContext] +def find(self, *, channel_id: str, thread_ts: str) ‑> AssistantThreadContext | None
    @@ -4128,13 +4128,13 @@

    Class variables

    Methods

    -def matches(self, *, req: BoltRequest, resp: BoltResponse) ‑> bool +def matches(self,
    *,
    req: BoltRequest,
    resp: BoltResponse) ‑> bool
    -def run_ack_function(self, *, request: BoltRequest, response: BoltResponse) ‑> Optional[BoltResponse] +def run_ack_function(self,
    *,
    request: BoltRequest,
    response: BoltResponse) ‑> BoltResponse | None

    Runs all the registered middleware and then run the listener function.

    @@ -4149,7 +4149,7 @@

    Returns

    The processed response

    -def run_middleware(self, *, req: BoltRequest, resp: BoltResponse) ‑> Tuple[Optional[BoltResponse], bool] +def run_middleware(self,
    *,
    req: BoltRequest,
    resp: BoltResponse) ‑> Tuple[BoltResponse | None, bool]

    Runs a middleware.

    @@ -4167,7 +4167,7 @@

    Returns

    class Respond -(*, response_url: Optional[str], proxy: Optional[str] = None, ssl: Optional[ssl.SSLContext] = None) +(*,
    response_url: str | None,
    proxy: str | None = None,
    ssl: ssl.SSLContext | None = None)
    @@ -4236,15 +4236,15 @@

    Returns

    Class variables

    -
    var proxy : Optional[str]
    +
    var proxy : str | None
    -
    var response_url : Optional[str]
    +
    var response_url : str | None
    -
    var ssl : Optional[ssl.SSLContext]
    +
    var ssl : ssl.SSLContext | None
    @@ -4252,7 +4252,7 @@

    Class variables

    class SaveThreadContext -(thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str) +(thread_context_store: AssistantThreadContextStore,
    channel_id: str,
    thread_ts: str)
    @@ -4300,7 +4300,7 @@

    Class variables

    class Say -(client: Optional[slack_sdk.web.client.WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)] = None, build_metadata: Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]] = None) +(client: slack_sdk.web.client.WebClient | None,
    channel: str | None,
    thread_ts: str | None = None,
    metadata: Dict | slack_sdk.models.metadata.Metadata | None = None,
    build_metadata: Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None = None)
    @@ -4391,23 +4391,23 @@

    Class variables

    Class variables

    -
    var build_metadata : Optional[Callable[[], Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]]]
    +
    var build_metadata : Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None
    -
    var channel : Optional[str]
    +
    var channel : str | None
    -
    var client : Optional[slack_sdk.web.client.WebClient]
    +
    var client : slack_sdk.web.client.WebClient | None
    -
    var metadata : Union[Dict, slack_sdk.models.metadata.Metadata, ForwardRef(None)]
    +
    var metadata : Dict | slack_sdk.models.metadata.Metadata | None
    -
    var thread_ts : Optional[str]
    +
    var thread_ts : str | None
    @@ -4486,7 +4486,11 @@

    Class variables

    self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, prompts: List[Union[str, Dict[str, str]]]) -> SlackResponse: + def __call__( + self, + prompts: List[Union[str, Dict[str, str]]], + title: Optional[str] = None, + ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): @@ -4498,6 +4502,7 @@

    Class variables

    channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, + title=title, )

    Class variables

    @@ -4846,7 +4851,7 @@

    SetTitle diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/args.html b/docs/static/api-docs/slack_bolt/kwargs_injection/args.html index 6768b4230..ee9302825 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/args.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/args.html @@ -3,13 +3,13 @@ - + slack_bolt.kwargs_injection.args API documentation - + @@ -37,7 +37,7 @@

    Classes

    class Args -(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, next: Callable[[], None], **kwargs) +(*,
    logger: logging.Logger,
    client: slack_sdk.web.client.WebClient,
    req: BoltRequest,
    resp: BoltResponse,
    context: BoltContext,
    body: Dict[str, Any],
    payload: Dict[str, Any],
    options: Dict[str, Any] | None = None,
    shortcut: Dict[str, Any] | None = None,
    action: Dict[str, Any] | None = None,
    view: Dict[str, Any] | None = None,
    command: Dict[str, Any] | None = None,
    event: Dict[str, Any] | None = None,
    message: Dict[str, Any] | None = None,
    ack: Ack,
    say: Say,
    respond: Respond,
    complete: Complete,
    fail: Fail,
    set_status: SetStatus | None = None,
    set_title: SetTitle | None = None,
    set_suggested_prompts: SetSuggestedPrompts | None = None,
    get_thread_context: GetThreadContext | None = None,
    save_thread_context: SaveThreadContext | None = None,
    next: Callable[[], None],
    **kwargs)

    All the arguments in this class are available in any middleware / listeners. @@ -229,7 +229,7 @@

    Class variables

    ack() utility function, which returns acknowledgement to the Slack servers

    -
    var action : Optional[Dict[str, Any]]
    +
    var action : Dict[str, Any] | None

    An alias for payload in an @app.action listener

    @@ -241,7 +241,7 @@

    Class variables

    slack_sdk.web.WebClient instance with a valid token

    -
    var command : Optional[Dict[str, Any]]
    +
    var command : Dict[str, Any] | None

    An alias for payload in an @app.command listener

    @@ -253,7 +253,7 @@

    Class variables

    Context data associated with the incoming request

    -
    var event : Optional[Dict[str, Any]]
    +
    var event : Dict[str, Any] | None

    An alias for payload in an @app.event listener

    @@ -261,7 +261,7 @@

    Class variables

    fail() utility function, signal that the custom function failed to complete

    -
    var get_thread_context : Optional[GetThreadContext]
    +
    var get_thread_contextGetThreadContext | None

    get_thread_context() utility function for AI Agents & Assistants

    @@ -269,7 +269,7 @@

    Class variables

    Logger instance

    -
    var message : Optional[Dict[str, Any]]
    +
    var message : Dict[str, Any] | None

    An alias for payload in an @app.message listener

    @@ -281,7 +281,7 @@

    Class variables

    An alias of next() for avoiding the Python built-in method overrides in middleware functions

    -
    var options : Optional[Dict[str, Any]]
    +
    var options : Dict[str, Any] | None

    An alias for payload in an @app.options listener

    @@ -309,7 +309,7 @@

    Class variables

    Response representation

    -
    var save_thread_context : Optional[SaveThreadContext]
    +
    var save_thread_contextSaveThreadContext | None

    save_thread_context() utility function for AI Agents & Assistants

    @@ -317,23 +317,23 @@

    Class variables

    say() utility function, which calls chat.postMessage API with the associated channel ID

    -
    var set_status : Optional[SetStatus]
    +
    var set_statusSetStatus | None

    set_status() utility function for AI Agents & Assistants

    -
    var set_suggested_prompts : Optional[SetSuggestedPrompts]
    +
    var set_suggested_promptsSetSuggestedPrompts | None

    set_suggested_prompts() utility function for AI Agents & Assistants

    -
    var set_title : Optional[SetTitle]
    +
    var set_titleSetTitle | None

    set_title() utility function for AI Agents & Assistants

    -
    var shortcut : Optional[Dict[str, Any]]
    +
    var shortcut : Dict[str, Any] | None

    An alias for payload in an @app.shortcut listener

    -
    var view : Optional[Dict[str, Any]]
    +
    var view : Dict[str, Any] | None

    An alias for payload in an @app.view listener

    @@ -393,7 +393,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html b/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html index 08aa83cbf..97bc4b328 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html @@ -3,13 +3,13 @@ - + slack_bolt.kwargs_injection.async_args API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncArgs -(*, logger: logging.Logger, client: slack_sdk.web.async_client.AsyncWebClient, req: AsyncBoltRequest, resp: BoltResponse, context: AsyncBoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: AsyncAck, say: AsyncSay, respond: AsyncRespond, complete: AsyncComplete, fail: AsyncFail, set_status: Optional[AsyncSetStatus] = None, set_title: Optional[AsyncSetTitle] = None, set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] = None, get_thread_context: Optional[AsyncGetThreadContext] = None, save_thread_context: Optional[AsyncSaveThreadContext] = None, next: Callable[[], Awaitable[None]], **kwargs) +(*,
    logger: logging.Logger,
    client: slack_sdk.web.async_client.AsyncWebClient,
    req: AsyncBoltRequest,
    resp: BoltResponse,
    context: AsyncBoltContext,
    body: Dict[str, Any],
    payload: Dict[str, Any],
    options: Dict[str, Any] | None = None,
    shortcut: Dict[str, Any] | None = None,
    action: Dict[str, Any] | None = None,
    view: Dict[str, Any] | None = None,
    command: Dict[str, Any] | None = None,
    event: Dict[str, Any] | None = None,
    message: Dict[str, Any] | None = None,
    ack: AsyncAck,
    say: AsyncSay,
    respond: AsyncRespond,
    complete: AsyncComplete,
    fail: AsyncFail,
    set_status: AsyncSetStatus | None = None,
    set_title: AsyncSetTitle | None = None,
    set_suggested_prompts: AsyncSetSuggestedPrompts | None = None,
    get_thread_context: AsyncGetThreadContext | None = None,
    save_thread_context: AsyncSaveThreadContext | None = None,
    next: Callable[[], Awaitable[None]],
    **kwargs)

    All the arguments in this class are available in any middleware / listeners. @@ -226,7 +226,7 @@

    Class variables

    ack() utility function, which returns acknowledgement to the Slack servers

    -
    var action : Optional[Dict[str, Any]]
    +
    var action : Dict[str, Any] | None

    An alias for payload in an @app.action listener

    @@ -238,7 +238,7 @@

    Class variables

    slack_sdk.web.async_client.AsyncWebClient instance with a valid token

    -
    var command : Optional[Dict[str, Any]]
    +
    var command : Dict[str, Any] | None

    An alias for payload in an @app.command listener

    @@ -250,7 +250,7 @@

    Class variables

    Context data associated with the incoming request

    -
    var event : Optional[Dict[str, Any]]
    +
    var event : Dict[str, Any] | None

    An alias for payload in an @app.event listener

    @@ -258,7 +258,7 @@

    Class variables

    fail() utility function, signal that the custom function failed to complete

    -
    var get_thread_context : Optional[AsyncGetThreadContext]
    +
    var get_thread_contextAsyncGetThreadContext | None

    get_thread_context() utility function for AI Agents & Assistants

    @@ -266,7 +266,7 @@

    Class variables

    Logger instance

    -
    var message : Optional[Dict[str, Any]]
    +
    var message : Dict[str, Any] | None

    An alias for payload in an @app.message listener

    @@ -278,7 +278,7 @@

    Class variables

    An alias of next() for avoiding the Python built-in method overrides in middleware functions

    -
    var options : Optional[Dict[str, Any]]
    +
    var options : Dict[str, Any] | None

    An alias for payload in an @app.options listener

    @@ -306,7 +306,7 @@

    Class variables

    Response representation

    -
    var save_thread_context : Optional[AsyncSaveThreadContext]
    +
    var save_thread_contextAsyncSaveThreadContext | None

    save_thread_context() utility function for AI Agents & Assistants

    @@ -314,23 +314,23 @@

    Class variables

    say() utility function, which calls chat.postMessage API with the associated channel ID

    -
    var set_status : Optional[AsyncSetStatus]
    +
    var set_statusAsyncSetStatus | None

    set_status() utility function for AI Agents & Assistants

    -
    var set_suggested_prompts : Optional[AsyncSetSuggestedPrompts]
    +
    var set_suggested_promptsAsyncSetSuggestedPrompts | None

    set_suggested_prompts() utility function for AI Agents & Assistants

    -
    var set_title : Optional[AsyncSetTitle]
    +
    var set_titleAsyncSetTitle | None

    set_title() utility function for AI Agents & Assistants

    -
    var shortcut : Optional[Dict[str, Any]]
    +
    var shortcut : Dict[str, Any] | None

    An alias for payload in an @app.shortcut listener

    -
    var view : Optional[Dict[str, Any]]
    +
    var view : Dict[str, Any] | None

    An alias for payload in an @app.view listener

    @@ -390,7 +390,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html b/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html index 49e597234..368050d29 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html @@ -3,13 +3,13 @@ - + slack_bolt.kwargs_injection.async_utils API documentation - + @@ -34,7 +34,7 @@

    Module slack_bolt.kwargs_injection.async_utilsFunctions

    -def build_async_required_kwargs(*, logger: logging.Logger, required_arg_names: MutableSequence[str], request: AsyncBoltRequest, response: Optional[BoltResponse], next_func: Optional[Callable[[], None]] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any] +def build_async_required_kwargs(*,
    logger: logging.Logger,
    required_arg_names: MutableSequence[str],
    request: AsyncBoltRequest,
    response: BoltResponse | None,
    next_func: Callable[[], None] | None = None,
    this_func: Callable | None = None,
    error: Exception | None = None,
    next_keys_required: bool = True) ‑> Dict[str, Any]
    @@ -63,7 +63,7 @@

    Functions

    diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/index.html b/docs/static/api-docs/slack_bolt/kwargs_injection/index.html index 695b30653..850be4834 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/index.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/index.html @@ -3,13 +3,13 @@ - + slack_bolt.kwargs_injection API documentation - + @@ -56,7 +56,7 @@

    Sub-modules

    Functions

    -def build_required_kwargs(*, logger: logging.Logger, required_arg_names: MutableSequence[str], request: BoltRequest, response: Optional[BoltResponse], next_func: Optional[Callable[[], None]] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any] +def build_required_kwargs(*,
    logger: logging.Logger,
    required_arg_names: MutableSequence[str],
    request: BoltRequest,
    response: BoltResponse | None,
    next_func: Callable[[], None] | None = None,
    this_func: Callable | None = None,
    error: Exception | None = None,
    next_keys_required: bool = True) ‑> Dict[str, Any]
    @@ -68,7 +68,7 @@

    Classes

    class Args -(*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, next: Callable[[], None], **kwargs) +(*,
    logger: logging.Logger,
    client: slack_sdk.web.client.WebClient,
    req: BoltRequest,
    resp: BoltResponse,
    context: BoltContext,
    body: Dict[str, Any],
    payload: Dict[str, Any],
    options: Dict[str, Any] | None = None,
    shortcut: Dict[str, Any] | None = None,
    action: Dict[str, Any] | None = None,
    view: Dict[str, Any] | None = None,
    command: Dict[str, Any] | None = None,
    event: Dict[str, Any] | None = None,
    message: Dict[str, Any] | None = None,
    ack: Ack,
    say: Say,
    respond: Respond,
    complete: Complete,
    fail: Fail,
    set_status: SetStatus | None = None,
    set_title: SetTitle | None = None,
    set_suggested_prompts: SetSuggestedPrompts | None = None,
    get_thread_context: GetThreadContext | None = None,
    save_thread_context: SaveThreadContext | None = None,
    next: Callable[[], None],
    **kwargs)

    All the arguments in this class are available in any middleware / listeners. @@ -260,7 +260,7 @@

    Class variables

    ack() utility function, which returns acknowledgement to the Slack servers

    -
    var action : Optional[Dict[str, Any]]
    +
    var action : Dict[str, Any] | None

    An alias for payload in an @app.action listener

    @@ -272,7 +272,7 @@

    Class variables

    slack_sdk.web.WebClient instance with a valid token

    -
    var command : Optional[Dict[str, Any]]
    +
    var command : Dict[str, Any] | None

    An alias for payload in an @app.command listener

    @@ -284,7 +284,7 @@

    Class variables

    Context data associated with the incoming request

    -
    var event : Optional[Dict[str, Any]]
    +
    var event : Dict[str, Any] | None

    An alias for payload in an @app.event listener

    @@ -292,7 +292,7 @@

    Class variables

    fail() utility function, signal that the custom function failed to complete

    -
    var get_thread_context : Optional[GetThreadContext]
    +
    var get_thread_contextGetThreadContext | None

    get_thread_context() utility function for AI Agents & Assistants

    @@ -300,7 +300,7 @@

    Class variables

    Logger instance

    -
    var message : Optional[Dict[str, Any]]
    +
    var message : Dict[str, Any] | None

    An alias for payload in an @app.message listener

    @@ -312,7 +312,7 @@

    Class variables

    An alias of next() for avoiding the Python built-in method overrides in middleware functions

    -
    var options : Optional[Dict[str, Any]]
    +
    var options : Dict[str, Any] | None

    An alias for payload in an @app.options listener

    @@ -340,7 +340,7 @@

    Class variables

    Response representation

    -
    var save_thread_context : Optional[SaveThreadContext]
    +
    var save_thread_contextSaveThreadContext | None

    save_thread_context() utility function for AI Agents & Assistants

    @@ -348,23 +348,23 @@

    Class variables

    say() utility function, which calls chat.postMessage API with the associated channel ID

    -
    var set_status : Optional[SetStatus]
    +
    var set_statusSetStatus | None

    set_status() utility function for AI Agents & Assistants

    -
    var set_suggested_prompts : Optional[SetSuggestedPrompts]
    +
    var set_suggested_promptsSetSuggestedPrompts | None

    set_suggested_prompts() utility function for AI Agents & Assistants

    -
    var set_title : Optional[SetTitle]
    +
    var set_titleSetTitle | None

    set_title() utility function for AI Agents & Assistants

    -
    var shortcut : Optional[Dict[str, Any]]
    +
    var shortcut : Dict[str, Any] | None

    An alias for payload in an @app.shortcut listener

    -
    var view : Optional[Dict[str, Any]]
    +
    var view : Dict[str, Any] | None

    An alias for payload in an @app.view listener

    @@ -437,7 +437,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html b/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html index 443b6a89f..b54128278 100644 --- a/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html +++ b/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html @@ -3,13 +3,13 @@ - + slack_bolt.kwargs_injection.utils API documentation - + @@ -34,7 +34,7 @@

    Module slack_bolt.kwargs_injection.utils

    Functions

    -def build_required_kwargs(*, logger: logging.Logger, required_arg_names: MutableSequence[str], request: BoltRequest, response: Optional[BoltResponse], next_func: Optional[Callable[[], None]] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, next_keys_required: bool = True) ‑> Dict[str, Any] +def build_required_kwargs(*,
    logger: logging.Logger,
    required_arg_names: MutableSequence[str],
    request: BoltRequest,
    response: BoltResponse | None,
    next_func: Callable[[], None] | None = None,
    this_func: Callable | None = None,
    error: Exception | None = None,
    next_keys_required: bool = True) ‑> Dict[str, Any]
    @@ -63,7 +63,7 @@

    Functions

    diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/async_internals.html b/docs/static/api-docs/slack_bolt/lazy_listener/async_internals.html index 8e6355160..f1c66f368 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/async_internals.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/async_internals.html @@ -3,13 +3,13 @@ - + slack_bolt.lazy_listener.async_internals API documentation - + @@ -34,7 +34,7 @@

    Module slack_bolt.lazy_listener.async_internalsFunctions

    -async def to_runnable_function(internal_func: Callable[..., Awaitable[None]], logger: logging.Logger, request: AsyncBoltRequest) +async def to_runnable_function(internal_func: Callable[..., Awaitable[None]],
    logger: logging.Logger,
    request: AsyncBoltRequest)
    @@ -63,7 +63,7 @@

    Functions

    diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html b/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html index 389ae4cfa..32b2fec7b 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html @@ -3,13 +3,13 @@ - + slack_bolt.lazy_listener.async_runner API documentation - + @@ -85,7 +85,7 @@

    Class variables

    Methods

    -async def run(self, function: Callable[..., Awaitable[None]], request: AsyncBoltRequest) ‑> None +async def run(self,
    function: Callable[..., Awaitable[None]],
    request: AsyncBoltRequest) ‑> None

    Synchronously run the function with a given request data.

    @@ -98,7 +98,7 @@

    Args

    -def start(self, function: Callable[..., Awaitable[None]], request: AsyncBoltRequest) ‑> None +def start(self,
    function: Callable[..., Awaitable[None]],
    request: AsyncBoltRequest) ‑> None

    Starts a new lazy listener execution.

    @@ -141,7 +141,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/asyncio_runner.html b/docs/static/api-docs/slack_bolt/lazy_listener/asyncio_runner.html index 19a356281..562e2457e 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/asyncio_runner.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/asyncio_runner.html @@ -3,13 +3,13 @@ - + slack_bolt.lazy_listener.asyncio_runner API documentation - + @@ -111,7 +111,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/index.html b/docs/static/api-docs/slack_bolt/lazy_listener/index.html index 834ebff55..d7c866b20 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/index.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/index.html @@ -3,13 +3,13 @@ - + slack_bolt.lazy_listener API documentation - + @@ -135,7 +135,7 @@

    Class variables

    Methods

    -def run(self, function: Callable[..., None], request: BoltRequest) ‑> None +def run(self,
    function: Callable[..., None],
    request: BoltRequest) ‑> None

    Synchronously runs the function with a given request data.

    @@ -148,7 +148,7 @@

    Args

    -def start(self, function: Callable[..., None], request: BoltRequest) ‑> None +def start(self,
    function: Callable[..., None],
    request: BoltRequest) ‑> None

    Starts a new lazy listener execution.

    @@ -262,7 +262,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/internals.html b/docs/static/api-docs/slack_bolt/lazy_listener/internals.html index dbf56352d..32e7c59f3 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/internals.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.lazy_listener.internals API documentation - + @@ -34,7 +34,7 @@

    Module slack_bolt.lazy_listener.internals

    Functions

    -def build_runnable_function(func: Callable[..., None], logger: logging.Logger, request: BoltRequest) ‑> Callable[[], None] +def build_runnable_function(func: Callable[..., None],
    logger: logging.Logger,
    request: BoltRequest) ‑> Callable[[], None]
    @@ -63,7 +63,7 @@

    Functions

    diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/runner.html b/docs/static/api-docs/slack_bolt/lazy_listener/runner.html index 8315ef564..6db474138 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/runner.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/runner.html @@ -3,13 +3,13 @@ - + slack_bolt.lazy_listener.runner API documentation - + @@ -87,7 +87,7 @@

    Class variables

    Methods

    -def run(self, function: Callable[..., None], request: BoltRequest) ‑> None +def run(self,
    function: Callable[..., None],
    request: BoltRequest) ‑> None

    Synchronously runs the function with a given request data.

    @@ -100,7 +100,7 @@

    Args

    -def start(self, function: Callable[..., None], request: BoltRequest) ‑> None +def start(self,
    function: Callable[..., None],
    request: BoltRequest) ‑> None

    Starts a new lazy listener execution.

    @@ -143,7 +143,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/thread_runner.html b/docs/static/api-docs/slack_bolt/lazy_listener/thread_runner.html index 6af35ecc2..d0b69e6d0 100644 --- a/docs/static/api-docs/slack_bolt/lazy_listener/thread_runner.html +++ b/docs/static/api-docs/slack_bolt/lazy_listener/thread_runner.html @@ -3,13 +3,13 @@ - + slack_bolt.lazy_listener.thread_runner API documentation - + @@ -117,7 +117,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/async_builtins.html b/docs/static/api-docs/slack_bolt/listener/async_builtins.html index 94dc7f695..d4aa05a7a 100644 --- a/docs/static/api-docs/slack_bolt/listener/async_builtins.html +++ b/docs/static/api-docs/slack_bolt/listener/async_builtins.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.async_builtins API documentation - + @@ -85,13 +85,13 @@

    Class variables

    Methods

    -async def handle_app_uninstalled_events(self, context: AsyncBoltContext) ‑> None +async def handle_app_uninstalled_events(self,
    context: AsyncBoltContext) ‑> None
    -async def handle_tokens_revoked_events(self, event: dict, context: AsyncBoltContext) ‑> None +async def handle_tokens_revoked_events(self,
    event: dict,
    context: AsyncBoltContext) ‑> None
    @@ -127,7 +127,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener.html b/docs/static/api-docs/slack_bolt/listener/async_listener.html index 6b755c213..53d1dbdf6 100644 --- a/docs/static/api-docs/slack_bolt/listener/async_listener.html +++ b/docs/static/api-docs/slack_bolt/listener/async_listener.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.async_listener API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncCustomListener -(*, app_name: str, ack_function: Callable[..., Awaitable[Optional[BoltResponse]]], lazy_functions: Sequence[Callable[..., Awaitable[None]]], matchers: Sequence[AsyncListenerMatcher], middleware: Sequence[AsyncMiddleware], auto_acknowledgement: bool = False, base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    ack_function: Callable[..., Awaitable[BoltResponse | None]],
    lazy_functions: Sequence[Callable[..., Awaitable[None]]],
    matchers: Sequence[AsyncListenerMatcher],
    middleware: Sequence[AsyncMiddleware],
    auto_acknowledgement: bool = False,
    base_logger: logging.Logger | None = None)
    @@ -97,7 +97,7 @@

    Ancestors

    Class variables

    -
    var ack_function : Callable[..., Awaitable[Optional[BoltResponse]]]
    +
    var ack_function : Callable[..., Awaitable[BoltResponse | None]]
    @@ -133,7 +133,7 @@

    Class variables

    Methods

    -async def run_ack_function(self, *, request: AsyncBoltRequest, response: BoltResponse) ‑> Optional[BoltResponse] +async def run_ack_function(self,
    *,
    request: AsyncBoltRequest,
    response: BoltResponse) ‑> BoltResponse | None

    Runs all the registered middleware and then run the listener function.

    @@ -151,7 +151,7 @@

    Returns

    class cls -(*, app_name: str, ack_function: Callable[..., Awaitable[Optional[BoltResponse]]], lazy_functions: Sequence[Callable[..., Awaitable[None]]], matchers: Sequence[AsyncListenerMatcher], middleware: Sequence[AsyncMiddleware], auto_acknowledgement: bool = False, base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    ack_function: Callable[..., Awaitable[BoltResponse | None]],
    lazy_functions: Sequence[Callable[..., Awaitable[None]]],
    matchers: Sequence[AsyncListenerMatcher],
    middleware: Sequence[AsyncMiddleware],
    auto_acknowledgement: bool = False,
    base_logger: logging.Logger | None = None)
    @@ -211,7 +211,7 @@

    Ancestors

    Class variables

    -
    var ack_function : Callable[..., Awaitable[Optional[BoltResponse]]]
    +
    var ack_function : Callable[..., Awaitable[BoltResponse | None]]
    @@ -353,13 +353,13 @@

    Class variables

    Methods

    -async def async_matches(self, *, req: AsyncBoltRequest, resp: BoltResponse) ‑> bool +async def async_matches(self,
    *,
    req: AsyncBoltRequest,
    resp: BoltResponse) ‑> bool
    -async def run_ack_function(self, *, request: AsyncBoltRequest, response: BoltResponse) ‑> Optional[BoltResponse] +async def run_ack_function(self,
    *,
    request: AsyncBoltRequest,
    response: BoltResponse) ‑> BoltResponse | None

    Runs all the registered middleware and then run the listener function.

    @@ -374,7 +374,7 @@

    Returns

    The processed response

    -async def run_async_middleware(self, *, req: AsyncBoltRequest, resp: BoltResponse) ‑> Tuple[Optional[BoltResponse], bool] +async def run_async_middleware(self,
    *,
    req: AsyncBoltRequest,
    resp: BoltResponse) ‑> Tuple[BoltResponse | None, bool]

    Runs an async middleware.

    @@ -451,7 +451,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener_completion_handler.html b/docs/static/api-docs/slack_bolt/listener/async_listener_completion_handler.html index aa3174035..b76395821 100644 --- a/docs/static/api-docs/slack_bolt/listener/async_listener_completion_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/async_listener_completion_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.async_listener_completion_handler API documentation - + @@ -144,7 +144,7 @@

    Subclasses

    Methods

    -async def handle(self, request: AsyncBoltRequest, response: Optional[BoltResponse]) ‑> None +async def handle(self,
    request: AsyncBoltRequest,
    response: BoltResponse | None) ‑> None

    Do something extra after the listener execution

    @@ -191,7 +191,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html b/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html index 97a356f5d..f752e4c9f 100644 --- a/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.async_listener_error_handler API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncCustomListenerErrorHandler -(logger: logging.Logger, func: Callable[..., Awaitable[Optional[BoltResponse]]]) +(logger: logging.Logger,
    func: Callable[..., Awaitable[BoltResponse | None]])
    @@ -154,7 +154,7 @@

    Subclasses

    Methods

    -async def handle(self, error: Exception, request: AsyncBoltRequest, response: Optional[BoltResponse]) ‑> None +async def handle(self,
    error: Exception,
    request: AsyncBoltRequest,
    response: BoltResponse | None) ‑> None

    Handles an unhandled exception.

    @@ -203,7 +203,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener_start_handler.html b/docs/static/api-docs/slack_bolt/listener/async_listener_start_handler.html index ddb6c5538..a2638ebc2 100644 --- a/docs/static/api-docs/slack_bolt/listener/async_listener_start_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/async_listener_start_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.async_listener_start_handler API documentation - + @@ -144,7 +144,7 @@

    Subclasses

    Methods

    -async def handle(self, request: AsyncBoltRequest, response: Optional[BoltResponse]) ‑> None +async def handle(self,
    request: AsyncBoltRequest,
    response: BoltResponse | None) ‑> None

    Do something extra before the listener execution

    @@ -191,7 +191,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html b/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html index 389fbb824..2a1e50c04 100644 --- a/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html +++ b/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.asyncio_runner API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncioListenerRunner -(logger: logging.Logger, process_before_response: bool, listener_error_handler: AsyncListenerErrorHandler, listener_start_handler: AsyncListenerStartHandler, listener_completion_handler: AsyncListenerCompletionHandler, lazy_listener_runner: AsyncLazyListenerRunner) +(logger: logging.Logger,
    process_before_response: bool,
    listener_error_handler: AsyncListenerErrorHandler,
    listener_start_handler: AsyncListenerStartHandler,
    listener_completion_handler: AsyncListenerCompletionHandler,
    lazy_listener_runner: AsyncLazyListenerRunner)
    @@ -240,7 +240,7 @@

    Class variables

    Methods

    -async def run(self, request: AsyncBoltRequest, response: BoltResponse, listener_name: str, listener: AsyncListener, starting_time: Optional[float] = None) ‑> Optional[BoltResponse] +async def run(self,
    request: AsyncBoltRequest,
    response: BoltResponse,
    listener_name: str,
    listener: AsyncListener,
    starting_time: float | None = None) ‑> BoltResponse | None
    @@ -280,7 +280,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/listener/builtins.html b/docs/static/api-docs/slack_bolt/listener/builtins.html index d5ad2a391..93a5f1176 100644 --- a/docs/static/api-docs/slack_bolt/listener/builtins.html +++ b/docs/static/api-docs/slack_bolt/listener/builtins.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.builtins API documentation - + @@ -85,13 +85,13 @@

    Class variables

    Methods

    -def handle_app_uninstalled_events(self, context: BoltContext) ‑> None +def handle_app_uninstalled_events(self,
    context: BoltContext) ‑> None
    -def handle_tokens_revoked_events(self, event: dict, context: BoltContext) ‑> None +def handle_tokens_revoked_events(self,
    event: dict,
    context: BoltContext) ‑> None
    @@ -127,7 +127,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/listener/custom_listener.html b/docs/static/api-docs/slack_bolt/listener/custom_listener.html index a21bf9470..48fd83f4c 100644 --- a/docs/static/api-docs/slack_bolt/listener/custom_listener.html +++ b/docs/static/api-docs/slack_bolt/listener/custom_listener.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.custom_listener API documentation - + @@ -37,7 +37,7 @@

    Classes

    class CustomListener -(*, app_name: str, ack_function: Callable[..., Optional[BoltResponse]], lazy_functions: Sequence[Callable[..., None]], matchers: Sequence[ListenerMatcher], middleware: Sequence[Middleware], auto_acknowledgement: bool = False, base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    ack_function: Callable[..., BoltResponse | None],
    lazy_functions: Sequence[Callable[..., None]],
    matchers: Sequence[ListenerMatcher],
    middleware: Sequence[Middleware],
    auto_acknowledgement: bool = False,
    base_logger: logging.Logger | None = None)
    @@ -97,7 +97,7 @@

    Ancestors

    Class variables

    -
    var ack_function : Callable[..., Optional[BoltResponse]]
    +
    var ack_function : Callable[..., BoltResponse | None]
    @@ -174,7 +174,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/index.html b/docs/static/api-docs/slack_bolt/listener/index.html index 9fe889217..937708fd9 100644 --- a/docs/static/api-docs/slack_bolt/listener/index.html +++ b/docs/static/api-docs/slack_bolt/listener/index.html @@ -3,14 +3,14 @@ - + slack_bolt.listener API documentation - + @@ -96,7 +96,7 @@

    Classes

    class CustomListener -(*, app_name: str, ack_function: Callable[..., Optional[BoltResponse]], lazy_functions: Sequence[Callable[..., None]], matchers: Sequence[ListenerMatcher], middleware: Sequence[Middleware], auto_acknowledgement: bool = False, base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    ack_function: Callable[..., BoltResponse | None],
    lazy_functions: Sequence[Callable[..., None]],
    matchers: Sequence[ListenerMatcher],
    middleware: Sequence[Middleware],
    auto_acknowledgement: bool = False,
    base_logger: logging.Logger | None = None)
    @@ -156,7 +156,7 @@

    Ancestors

    Class variables

    -
    var ack_function : Callable[..., Optional[BoltResponse]]
    +
    var ack_function : Callable[..., BoltResponse | None]
    @@ -298,13 +298,13 @@

    Class variables

    Methods

    -def matches(self, *, req: BoltRequest, resp: BoltResponse) ‑> bool +def matches(self,
    *,
    req: BoltRequest,
    resp: BoltResponse) ‑> bool
    -def run_ack_function(self, *, request: BoltRequest, response: BoltResponse) ‑> Optional[BoltResponse] +def run_ack_function(self,
    *,
    request: BoltRequest,
    response: BoltResponse) ‑> BoltResponse | None

    Runs all the registered middleware and then run the listener function.

    @@ -319,7 +319,7 @@

    Returns

    The processed response

    -def run_middleware(self, *, req: BoltRequest, resp: BoltResponse) ‑> Tuple[Optional[BoltResponse], bool] +def run_middleware(self,
    *,
    req: BoltRequest,
    resp: BoltResponse) ‑> Tuple[BoltResponse | None, bool]

    Runs a middleware.

    @@ -399,7 +399,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/listener.html b/docs/static/api-docs/slack_bolt/listener/listener.html index d705207a8..45f190ff6 100644 --- a/docs/static/api-docs/slack_bolt/listener/listener.html +++ b/docs/static/api-docs/slack_bolt/listener/listener.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.listener API documentation - + @@ -134,13 +134,13 @@

    Class variables

    Methods

    -def matches(self, *, req: BoltRequest, resp: BoltResponse) ‑> bool +def matches(self,
    *,
    req: BoltRequest,
    resp: BoltResponse) ‑> bool
    -def run_ack_function(self, *, request: BoltRequest, response: BoltResponse) ‑> Optional[BoltResponse] +def run_ack_function(self,
    *,
    request: BoltRequest,
    response: BoltResponse) ‑> BoltResponse | None

    Runs all the registered middleware and then run the listener function.

    @@ -155,7 +155,7 @@

    Returns

    The processed response

    -def run_middleware(self, *, req: BoltRequest, resp: BoltResponse) ‑> Tuple[Optional[BoltResponse], bool] +def run_middleware(self,
    *,
    req: BoltRequest,
    resp: BoltResponse) ‑> Tuple[BoltResponse | None, bool]

    Runs a middleware.

    @@ -205,7 +205,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/listener_completion_handler.html b/docs/static/api-docs/slack_bolt/listener/listener_completion_handler.html index 80b5c5da6..7363ca43a 100644 --- a/docs/static/api-docs/slack_bolt/listener/listener_completion_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/listener_completion_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.listener_completion_handler API documentation - + @@ -145,7 +145,7 @@

    Subclasses

    Methods

    -def handle(self, request: BoltRequest, response: Optional[BoltResponse]) ‑> None +def handle(self,
    request: BoltRequest,
    response: BoltResponse | None) ‑> None

    Do something extra after the listener execution

    @@ -192,7 +192,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html b/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html index d7895959b..73b360ba2 100644 --- a/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.listener_error_handler API documentation - + @@ -37,7 +37,7 @@

    Classes

    class CustomListenerErrorHandler -(logger: logging.Logger, func: Callable[..., Optional[BoltResponse]]) +(logger: logging.Logger,
    func: Callable[..., BoltResponse | None])
    @@ -154,7 +154,7 @@

    Subclasses

    Methods

    -def handle(self, error: Exception, request: BoltRequest, response: Optional[BoltResponse]) ‑> None +def handle(self,
    error: Exception,
    request: BoltRequest,
    response: BoltResponse | None) ‑> None

    Handles an unhandled exception.

    @@ -203,7 +203,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/listener_start_handler.html b/docs/static/api-docs/slack_bolt/listener/listener_start_handler.html index cb0c75141..f5eccc473 100644 --- a/docs/static/api-docs/slack_bolt/listener/listener_start_handler.html +++ b/docs/static/api-docs/slack_bolt/listener/listener_start_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.listener_start_handler API documentation - + @@ -149,7 +149,7 @@

    Subclasses

    Methods

    -def handle(self, request: BoltRequest, response: Optional[BoltResponse]) ‑> None +def handle(self,
    request: BoltRequest,
    response: BoltResponse | None) ‑> None

    Do something extra before the listener execution.

    @@ -199,7 +199,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener/thread_runner.html b/docs/static/api-docs/slack_bolt/listener/thread_runner.html index 978561628..1dc97eaa4 100644 --- a/docs/static/api-docs/slack_bolt/listener/thread_runner.html +++ b/docs/static/api-docs/slack_bolt/listener/thread_runner.html @@ -3,13 +3,13 @@ - + slack_bolt.listener.thread_runner API documentation - + @@ -37,7 +37,7 @@

    Classes

    class ThreadListenerRunner -(logger: logging.Logger, process_before_response: bool, listener_error_handler: ListenerErrorHandler, listener_start_handler: ListenerStartHandler, listener_completion_handler: ListenerCompletionHandler, listener_executor: concurrent.futures._base.Executor, lazy_listener_runner: LazyListenerRunner) +(logger: logging.Logger,
    process_before_response: bool,
    listener_error_handler: ListenerErrorHandler,
    listener_start_handler: ListenerStartHandler,
    listener_completion_handler: ListenerCompletionHandler,
    listener_executor: concurrent.futures._base.Executor,
    lazy_listener_runner: LazyListenerRunner)
    @@ -262,7 +262,7 @@

    Class variables

    Methods

    -def run(self, request: BoltRequest, response: BoltResponse, listener_name: str, listener: Listener, starting_time: Optional[float] = None) ‑> Optional[BoltResponse] +def run(self,
    request: BoltRequest,
    response: BoltResponse,
    listener_name: str,
    listener: Listener,
    starting_time: float | None = None) ‑> BoltResponse | None
    @@ -303,7 +303,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html b/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html index 1ef4b5c63..ecc236cf3 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html @@ -3,13 +3,13 @@ - + slack_bolt.listener_matcher.async_builtins API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncBuiltinListenerMatcher -(*, func: Callable[..., Union[bool, Awaitable[bool]]], base_logger: Optional[logging.Logger] = None) +(*,
    func: Callable[..., bool | Awaitable[bool]],
    base_logger: logging.Logger | None = None)
    @@ -101,7 +101,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html b/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html index eba4cd7ad..04fcc5c4b 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html @@ -3,13 +3,13 @@ - + slack_bolt.listener_matcher.async_listener_matcher API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncCustomListenerMatcher -(*, app_name: str, func: Callable[..., Awaitable[bool]], base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    func: Callable[..., Awaitable[bool]],
    base_logger: logging.Logger | None = None)
    @@ -94,7 +94,7 @@

    Class variables

    Methods

    -async def async_matches(self, req: AsyncBoltRequest, resp: BoltResponse) ‑> bool +async def async_matches(self,
    req: AsyncBoltRequest,
    resp: BoltResponse) ‑> bool

    Matches against the request and returns True if matched.

    @@ -112,7 +112,7 @@

    Returns

    class cls -(*, app_name: str, func: Callable[..., Awaitable[bool]], base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    func: Callable[..., Awaitable[bool]],
    base_logger: logging.Logger | None = None)
    @@ -206,7 +206,7 @@

    Subclasses

    Methods

    -async def async_matches(self, req: AsyncBoltRequest, resp: BoltResponse) ‑> bool +async def async_matches(self,
    req: AsyncBoltRequest,
    resp: BoltResponse) ‑> bool

    Matches against the request and returns True if matched.

    @@ -268,7 +268,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html b/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html index 1b8f0f019..eb1b2a484 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html @@ -3,13 +3,13 @@ - + slack_bolt.listener_matcher.builtins API documentation - + @@ -34,133 +34,133 @@

    Module slack_bolt.listener_matcher.builtins

    Functions

    -def action(constraints: Union[str, re.Pattern, Dict[str, Union[str, re.Pattern]]], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def action(constraints: str | re.Pattern | Dict[str, str | re.Pattern],
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def attachment_action(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def attachment_action(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def block_action(constraints: Union[str, re.Pattern, Dict[str, Union[str, re.Pattern]]], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def block_action(constraints: str | re.Pattern | Dict[str, str | re.Pattern],
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def block_suggestion(action_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def block_suggestion(action_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def build_listener_matcher(func: Callable[..., bool], asyncio: bool, base_logger: Optional[logging.Logger] = None) +def build_listener_matcher(func: Callable[..., bool],
    asyncio: bool,
    base_logger: logging.Logger | None = None)
    -def command(command: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def command(command: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def dialog_cancellation(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def dialog_cancellation(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def dialog_submission(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def dialog_submission(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def dialog_suggestion(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def dialog_suggestion(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def event(constraints: Union[str, re.Pattern, Dict[str, Union[str, Sequence[Union[str, re.Pattern, ForwardRef(None)]], ForwardRef(None)]]], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def event(constraints: str | re.Pattern | Dict[str, str | Sequence[str | re.Pattern | None] | None],
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def function_executed(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def function_executed(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def global_shortcut(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def global_shortcut(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def message_event(constraints: Dict[str, Union[str, Sequence[Union[str, re.Pattern, ForwardRef(None)]], ForwardRef(None)]], keyword: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def message_event(constraints: Dict[str, str | Sequence[str | re.Pattern | None] | None],
    keyword: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def message_shortcut(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def message_shortcut(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def options(constraints: Union[str, re.Pattern, Dict[str, Union[str, re.Pattern]]], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def options(constraints: str | re.Pattern | Dict[str, str | re.Pattern],
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def shortcut(constraints: Union[str, re.Pattern, Dict[str, Union[str, re.Pattern]]], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def shortcut(constraints: str | re.Pattern | Dict[str, str | re.Pattern],
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def view(constraints: Union[str, re.Pattern, Dict[str, Union[str, re.Pattern]]], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def view(constraints: str | re.Pattern | Dict[str, str | re.Pattern],
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def view_closed(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def view_closed(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def view_submission(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def view_submission(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def workflow_step_edit(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def workflow_step_edit(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def workflow_step_execute(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def workflow_step_execute(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    -def workflow_step_save(callback_id: Union[str, re.Pattern], asyncio: bool = False, base_logger: Optional[logging.Logger] = None) +def workflow_step_save(callback_id: str | re.Pattern,
    asyncio: bool = False,
    base_logger: logging.Logger | None = None)
    @@ -172,7 +172,7 @@

    Classes

    class BuiltinListenerMatcher -(*, func: Callable[..., Union[bool, Awaitable[bool]]], base_logger: Optional[logging.Logger] = None) +(*,
    func: Callable[..., bool | Awaitable[bool]],
    base_logger: logging.Logger | None = None)
    @@ -269,7 +269,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html b/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html index c34cd0311..238665d8b 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html @@ -3,13 +3,13 @@ - + slack_bolt.listener_matcher.custom_listener_matcher API documentation - + @@ -37,7 +37,7 @@

    Classes

    class CustomListenerMatcher -(*, app_name: str, func: Callable[..., bool], base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    func: Callable[..., bool],
    base_logger: logging.Logger | None = None)
    @@ -130,7 +130,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/index.html b/docs/static/api-docs/slack_bolt/listener_matcher/index.html index 292e8836f..e9aef0881 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/index.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/index.html @@ -3,14 +3,14 @@ - + slack_bolt.listener_matcher API documentation - + @@ -64,7 +64,7 @@

    Classes

    class CustomListenerMatcher -(*, app_name: str, func: Callable[..., bool], base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    func: Callable[..., bool],
    base_logger: logging.Logger | None = None)
    @@ -158,7 +158,7 @@

    Subclasses

    Methods

    -def matches(self, req: BoltRequest, resp: BoltResponse) ‑> bool +def matches(self,
    req: BoltRequest,
    resp: BoltResponse) ‑> bool

    Matches against the request and returns True if matched.

    @@ -219,7 +219,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/listener_matcher.html b/docs/static/api-docs/slack_bolt/listener_matcher/listener_matcher.html index 846fdd154..8beab61c9 100644 --- a/docs/static/api-docs/slack_bolt/listener_matcher/listener_matcher.html +++ b/docs/static/api-docs/slack_bolt/listener_matcher/listener_matcher.html @@ -3,13 +3,13 @@ - + slack_bolt.listener_matcher.listener_matcher API documentation - + @@ -66,7 +66,7 @@

    Subclasses

    Methods

    -def matches(self, req: BoltRequest, resp: BoltResponse) ‑> bool +def matches(self,
    req: BoltRequest,
    resp: BoltResponse) ‑> bool

    Matches against the request and returns True if matched.

    @@ -109,7 +109,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/logger/index.html b/docs/static/api-docs/slack_bolt/logger/index.html index 2a34fb39c..825b07217 100644 --- a/docs/static/api-docs/slack_bolt/logger/index.html +++ b/docs/static/api-docs/slack_bolt/logger/index.html @@ -3,13 +3,13 @@ - + slack_bolt.logger API documentation - + @@ -42,13 +42,13 @@

    Sub-modules

    Functions

    -def get_bolt_app_logger(app_name: str, cls: object = None, base_logger: Optional[logging.Logger] = None) ‑> logging.Logger +def get_bolt_app_logger(app_name: str, cls: object = None, base_logger: logging.Logger | None = None) ‑> logging.Logger
    -def get_bolt_logger(cls: Any, base_logger: Optional[logging.Logger] = None) ‑> logging.Logger +def get_bolt_logger(cls: Any, base_logger: logging.Logger | None = None) ‑> logging.Logger
    @@ -83,7 +83,7 @@

    Functions

    diff --git a/docs/static/api-docs/slack_bolt/logger/messages.html b/docs/static/api-docs/slack_bolt/logger/messages.html index 9a6801595..37451045d 100644 --- a/docs/static/api-docs/slack_bolt/logger/messages.html +++ b/docs/static/api-docs/slack_bolt/logger/messages.html @@ -3,13 +3,13 @@ - + slack_bolt.logger.messages API documentation - + @@ -184,13 +184,13 @@

    Functions

    -def warning_unhandled_by_global_middleware(name: str, req: Union[BoltRequest, ForwardRef('AsyncBoltRequest')]) +def warning_unhandled_by_global_middleware(name: str,
    req: BoltRequest | ForwardRef('AsyncBoltRequest'))
    -def warning_unhandled_request(req: Union[BoltRequest, ForwardRef('AsyncBoltRequest')]) +def warning_unhandled_request(req: BoltRequest | ForwardRef('AsyncBoltRequest'))
    @@ -245,7 +245,7 @@

    Functions

    diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html b/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html index 31ecabdb1..da6aab008 100644 --- a/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html +++ b/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.assistant.assistant API documentation - + @@ -37,7 +37,7 @@

    Classes

    class Assistant -(*, app_name: str = 'assistant', thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +(*,
    app_name: str = 'assistant',
    thread_context_store: AssistantThreadContextStore | None = None,
    logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -315,11 +315,11 @@

    Ancestors

    Class variables

    -
    var base_logger : Optional[logging.Logger]
    +
    var base_logger : logging.Logger | None
    -
    var thread_context_store : Optional[AssistantThreadContextStore]
    +
    var thread_context_storeAssistantThreadContextStore | None
    @@ -327,7 +327,7 @@

    Class variables

    Static methods

    -def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict) +def default_thread_context_changed(save_thread_context: SaveThreadContext,
    payload: dict)
    @@ -336,31 +336,31 @@

    Static methods

    Methods

    -def bot_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def bot_message(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def build_listener(self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> Listener +def build_listener(self,
    listener_or_functions: Listener | Callable | List[Callable],
    matchers: List[ListenerMatcher | Callable[..., bool]] | None = None,
    middleware: List[Middleware] | None = None,
    base_logger: logging.Logger | None = None) ‑> Listener
    -def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_context_changed(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def thread_started(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_started(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def user_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def user_message(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    @@ -410,7 +410,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html b/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html index bf4ec0ce8..170f45d04 100644 --- a/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html +++ b/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.assistant.async_assistant API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncAssistant -(*, app_name: str = 'assistant', thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +(*,
    app_name: str = 'assistant',
    thread_context_store: AsyncAssistantThreadContextStore | None = None,
    logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -346,11 +346,11 @@

    Ancestors

    Class variables

    -
    var base_logger : Optional[logging.Logger]
    +
    var base_logger : logging.Logger | None
    -
    var thread_context_store : Optional[AsyncAssistantThreadContextStore]
    +
    var thread_context_storeAsyncAssistantThreadContextStore | None
    @@ -358,7 +358,7 @@

    Class variables

    Static methods

    -async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict) +async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext,
    payload: dict)
    @@ -367,31 +367,31 @@

    Static methods

    Methods

    -def bot_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def bot_message(self,
    *args,
    matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
    middleware: Callable | AsyncMiddleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def build_listener(self, listener_or_functions: Union[AsyncListener, Callable, List[Callable]], matchers: Optional[List[Union[AsyncListenerMatcher, Callable[..., Awaitable[bool]]]]] = None, middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> AsyncListener +def build_listener(self,
    listener_or_functions: AsyncListener | Callable | List[Callable],
    matchers: List[AsyncListenerMatcher | Callable[..., Awaitable[bool]]] | None = None,
    middleware: List[AsyncMiddleware] | None = None,
    base_logger: logging.Logger | None = None) ‑> AsyncListener
    -def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_context_changed(self,
    *args,
    matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
    middleware: Callable | AsyncMiddleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def thread_started(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_started(self,
    *args,
    matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
    middleware: Callable | AsyncMiddleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def user_message(self, *args, matchers: Union[Callable[..., bool], AsyncListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, AsyncMiddleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def user_message(self,
    *args,
    matchers: Callable[..., bool] | AsyncListenerMatcher | None = None,
    middleware: Callable | AsyncMiddleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    @@ -441,7 +441,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/index.html b/docs/static/api-docs/slack_bolt/middleware/assistant/index.html index 342b26182..8fb14d28e 100644 --- a/docs/static/api-docs/slack_bolt/middleware/assistant/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/assistant/index.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.assistant API documentation - + @@ -48,7 +48,7 @@

    Classes

    class Assistant -(*, app_name: str = 'assistant', thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None) +(*,
    app_name: str = 'assistant',
    thread_context_store: AssistantThreadContextStore | None = None,
    logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -326,11 +326,11 @@

    Ancestors

    Class variables

    -
    var base_logger : Optional[logging.Logger]
    +
    var base_logger : logging.Logger | None
    -
    var thread_context_store : Optional[AssistantThreadContextStore]
    +
    var thread_context_storeAssistantThreadContextStore | None
    @@ -338,7 +338,7 @@

    Class variables

    Static methods

    -def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict) +def default_thread_context_changed(save_thread_context: SaveThreadContext,
    payload: dict)
    @@ -347,31 +347,31 @@

    Static methods

    Methods

    -def bot_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def bot_message(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def build_listener(self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[logging.Logger] = None) ‑> Listener +def build_listener(self,
    listener_or_functions: Listener | Callable | List[Callable],
    matchers: List[ListenerMatcher | Callable[..., bool]] | None = None,
    middleware: List[Middleware] | None = None,
    base_logger: logging.Logger | None = None) ‑> Listener
    -def thread_context_changed(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_context_changed(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def thread_started(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def thread_started(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    -def user_message(self, *args, matchers: Union[Callable[..., bool], ListenerMatcher, ForwardRef(None)] = None, middleware: Union[Callable, Middleware, ForwardRef(None)] = None, lazy: Optional[List[Callable[..., None]]] = None) +def user_message(self,
    *args,
    matchers: Callable[..., bool] | ListenerMatcher | None = None,
    middleware: Callable | Middleware | None = None,
    lazy: List[Callable[..., None]] | None = None)
    @@ -427,7 +427,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/async_builtins.html b/docs/static/api-docs/slack_bolt/middleware/async_builtins.html index 5ef498865..f895481bf 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_builtins.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_builtins.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.async_builtins API documentation - + @@ -74,7 +74,7 @@

    Inherited members

    class AsyncIgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True) +(base_logger: logging.Logger | None = None,
    ignoring_self_assistant_message_events_enabled: bool = True)

    A middleware can process request data before other middleware and listener functions.

    @@ -128,7 +128,7 @@

    Inherited members

    class AsyncMessageListenerMatches -(keyword: Union[str, Pattern]) +(keyword: str | Pattern)

    A middleware can process request data before other middleware and listener functions.

    @@ -182,7 +182,7 @@

    Inherited members

    class AsyncRequestVerification -(signing_secret: str, base_logger: Optional[logging.Logger] = None) +(signing_secret: str, base_logger: logging.Logger | None = None)

    Verifies an incoming request by checking the validity of @@ -254,7 +254,7 @@

    Inherited members

    class AsyncSslCheck -(verification_token: Optional[str] = None, base_logger: Optional[logging.Logger] = None) +(verification_token: str | None = None,
    base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -296,17 +296,6 @@

    Ancestors

  • Middleware
  • AsyncMiddleware
  • -

    Class variables

    -
    -
    var logger : logging.Logger
    -
    -
    -
    -
    var verification_token : Optional[str]
    -
    -
    -
    -

    Inherited members

    • SslCheck: @@ -324,7 +313,7 @@

      Inherited members

    class AsyncUrlVerification -(base_logger: Optional[logging.Logger] = None) +(base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -405,10 +394,6 @@

    AsyncSslCheck

    -
  • AsyncUrlVerification

    @@ -419,7 +404,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html b/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html index 132eb6b41..e44955e00 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.async_custom_middleware API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncCustomMiddleware -(*, app_name: str, func: Callable[..., Awaitable[Any]], base_logger: Optional[logging.Logger] = None) +(*,
    app_name: str,
    func: Callable[..., Awaitable[Any]],
    base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -155,7 +155,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/async_middleware.html b/docs/static/api-docs/slack_bolt/middleware/async_middleware.html index 13704234c..90972540a 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_middleware.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_middleware.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.async_middleware API documentation - + @@ -121,7 +121,7 @@

    Instance variables

    Methods

    -async def async_process(self, *, req: AsyncBoltRequest, resp: BoltResponse, next: Callable[[], Awaitable[BoltResponse]]) ‑> Optional[BoltResponse] +async def async_process(self,
    *,
    req: AsyncBoltRequest,
    resp: BoltResponse,
    next: Callable[[], Awaitable[BoltResponse]]) ‑> BoltResponse | None

    Processes a request data before other middleware and listeners. @@ -180,7 +180,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html b/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html index f899b7264..f87f7e5e6 100644 --- a/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html +++ b/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.async_middleware_error_handler API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncCustomMiddlewareErrorHandler -(logger: logging.Logger, func: Callable[..., Awaitable[Optional[BoltResponse]]]) +(logger: logging.Logger,
    func: Callable[..., Awaitable[BoltResponse | None]])
    @@ -154,7 +154,7 @@

    Subclasses

    Methods

    -async def handle(self, error: Exception, request: AsyncBoltRequest, response: Optional[BoltResponse]) ‑> None +async def handle(self,
    error: Exception,
    request: AsyncBoltRequest,
    response: BoltResponse | None) ‑> None

    Handles an unhandled exception.

    @@ -203,7 +203,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html index 068be8cb2..41e3e8298 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.attaching_function_token.async_attaching_function_token API documentation - + @@ -96,7 +96,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html index bf323aead..7c46065e8 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.attaching_function_token.attaching_function_token API documentation - + @@ -96,7 +96,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html index 0ddd5b07f..a6d942973 100644 --- a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.attaching_function_token API documentation - + @@ -113,7 +113,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_authorization.html index 6ac3ccbce..7a959c98c 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_authorization.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.async_authorization API documentation - + @@ -91,7 +91,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_internals.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_internals.html index be69f523f..fb72b0390 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_internals.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_internals.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.async_internals API documentation - + @@ -49,7 +49,7 @@

    Module slack_bolt.middleware.authorization.async_interna diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html index 4f8be284d..57dd80426 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.async_multi_teams_authorization API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncMultiTeamsAuthorization -(authorize: AsyncAuthorize, base_logger: Optional[logging.Logger] = None, user_token_resolution: str = 'authed_user', user_facing_authorize_error_message: Optional[str] = None) +(authorize: AsyncAuthorize,
    base_logger: logging.Logger | None = None,
    user_token_resolution: str = 'authed_user',
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -205,7 +205,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html index aaa88cf6c..886574b0e 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.async_single_team_authorization API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncSingleTeamAuthorization -(base_logger: Optional[logging.Logger] = None, user_facing_authorize_error_message: Optional[str] = None) +(base_logger: logging.Logger | None = None,
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -146,7 +146,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/authorization.html index 230c84542..2a686434e 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/authorization.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.authorization API documentation - + @@ -90,7 +90,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/index.html b/docs/static/api-docs/slack_bolt/middleware/authorization/index.html index 9430bb94e..7aab586cc 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/index.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization API documentation - + @@ -103,7 +103,7 @@

    Inherited members

    class MultiTeamsAuthorization -(*, authorize: Authorize, base_logger: Optional[logging.Logger] = None, user_token_resolution: str = 'authed_user', user_facing_authorize_error_message: Optional[str] = None) +(*,
    authorize: Authorize,
    base_logger: logging.Logger | None = None,
    user_token_resolution: str = 'authed_user',
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -242,7 +242,7 @@

    Inherited members

    class SingleTeamAuthorization -(*, auth_test_result: Optional[slack_sdk.web.slack_response.SlackResponse] = None, base_logger: Optional[logging.Logger] = None, user_facing_authorize_error_message: Optional[str] = None) +(*,
    auth_test_result: slack_sdk.web.slack_response.SlackResponse | None = None,
    base_logger: logging.Logger | None = None,
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -387,7 +387,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/internals.html b/docs/static/api-docs/slack_bolt/middleware/authorization/internals.html index 0c776a062..8cda931f3 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/internals.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/internals.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.internals API documentation - + @@ -49,7 +49,7 @@

    Module slack_bolt.middleware.authorization.internals diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html index 722d89371..e16c2c3b7 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.multi_teams_authorization API documentation - + @@ -37,7 +37,7 @@

    Classes

    class MultiTeamsAuthorization -(*, authorize: Authorize, base_logger: Optional[logging.Logger] = None, user_token_resolution: str = 'authed_user', user_facing_authorize_error_message: Optional[str] = None) +(*,
    authorize: Authorize,
    base_logger: logging.Logger | None = None,
    user_token_resolution: str = 'authed_user',
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -202,7 +202,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html b/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html index a2ae9c009..e660424aa 100644 --- a/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html +++ b/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.authorization.single_team_authorization API documentation - + @@ -37,7 +37,7 @@

    Classes

    class SingleTeamAuthorization -(*, auth_test_result: Optional[slack_sdk.web.slack_response.SlackResponse] = None, base_logger: Optional[logging.Logger] = None, user_facing_authorize_error_message: Optional[str] = None) +(*,
    auth_test_result: slack_sdk.web.slack_response.SlackResponse | None = None,
    base_logger: logging.Logger | None = None,
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -160,7 +160,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html b/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html index fd8efc789..3c07434ac 100644 --- a/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html +++ b/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.custom_middleware API documentation - + @@ -37,7 +37,7 @@

    Classes

    class CustomMiddleware -(*, app_name: str, func: Callable, base_logger: Optional[logging.Logger] = None) +(*, app_name: str, func: Callable, base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -145,7 +145,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html index 0d549afc7..f47c02d2e 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncIgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True) +(base_logger: logging.Logger | None = None,
    ignoring_self_assistant_message_events_enabled: bool = True)

    A middleware can process request data before other middleware and listener functions.

    @@ -113,7 +113,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html index e22295094..8961f06c1 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.ignoring_self_events.ignoring_self_events API documentation - + @@ -37,7 +37,7 @@

    Classes

    class IgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True) +(base_logger: logging.Logger | None = None,
    ignoring_self_assistant_message_events_enabled: bool = True)

    A middleware can process request data before other middleware and listener functions.

    @@ -159,7 +159,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html index 853d1225c..72dade334 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.ignoring_self_events API documentation - + @@ -48,7 +48,7 @@

    Classes

    class IgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True) +(base_logger: logging.Logger | None = None,
    ignoring_self_assistant_message_events_enabled: bool = True)

    A middleware can process request data before other middleware and listener functions.

    @@ -176,7 +176,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/index.html b/docs/static/api-docs/slack_bolt/middleware/index.html index 2be166678..0e0ca818a 100644 --- a/docs/static/api-docs/slack_bolt/middleware/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/index.html @@ -3,14 +3,14 @@ - + slack_bolt.middleware API documentation - + @@ -142,7 +142,7 @@

    Inherited members

    class CustomMiddleware -(*, app_name: str, func: Callable, base_logger: Optional[logging.Logger] = None) +(*, app_name: str, func: Callable, base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -222,7 +222,7 @@

    Inherited members

    class IgnoringSelfEvents -(base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True) +(base_logger: logging.Logger | None = None,
    ignoring_self_assistant_message_events_enabled: bool = True)

    A middleware can process request data before other middleware and listener functions.

    @@ -403,7 +403,7 @@

    Instance variables

    Methods

    -def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) ‑> Optional[BoltResponse] +def process(self,
    *,
    req: BoltRequest,
    resp: BoltResponse,
    next: Callable[[], BoltResponse]) ‑> BoltResponse | None

    Processes a request data before other middleware and listeners. @@ -436,7 +436,7 @@

    Returns

    class MultiTeamsAuthorization -(*, authorize: Authorize, base_logger: Optional[logging.Logger] = None, user_token_resolution: str = 'authed_user', user_facing_authorize_error_message: Optional[str] = None) +(*,
    authorize: Authorize,
    base_logger: logging.Logger | None = None,
    user_token_resolution: str = 'authed_user',
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -575,7 +575,7 @@

    Inherited members

    class RequestVerification -(signing_secret: str, base_logger: Optional[logging.Logger] = None) +(signing_secret: str, base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -664,7 +664,7 @@

    Inherited members

    class SingleTeamAuthorization -(*, auth_test_result: Optional[slack_sdk.web.slack_response.SlackResponse] = None, base_logger: Optional[logging.Logger] = None, user_facing_authorize_error_message: Optional[str] = None) +(*,
    auth_test_result: slack_sdk.web.slack_response.SlackResponse | None = None,
    base_logger: logging.Logger | None = None,
    user_facing_authorize_error_message: str | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -765,7 +765,7 @@

    Inherited members

    class SslCheck -(verification_token: Optional[str] = None, base_logger: Optional[logging.Logger] = None) +(verification_token: str | None = None,
    base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -851,7 +851,7 @@

    Class variables

    -
    var verification_token : Optional[str]
    +
    var verification_token : str | None
    @@ -868,7 +868,7 @@

    Inherited members

    class UrlVerification -(base_logger: Optional[logging.Logger] = None) +(base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -1025,7 +1025,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html index 081535c83..6e3ed25b5 100644 --- a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html +++ b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.message_listener_matches.async_message_listener_matches API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncMessageListenerMatches -(keyword: Union[str, Pattern]) +(keyword: str | Pattern)

    A middleware can process request data before other middleware and listener functions.

    @@ -113,7 +113,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html index 7a49942c4..66b8211b7 100644 --- a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.message_listener_matches API documentation - + @@ -48,7 +48,7 @@

    Classes

    class MessageListenerMatches -(keyword: Union[str, Pattern]) +(keyword: str | Pattern)

    A middleware can process request data before other middleware and listener functions.

    @@ -130,7 +130,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html index 6e7716218..65a624297 100644 --- a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html +++ b/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.message_listener_matches.message_listener_matches API documentation - + @@ -37,7 +37,7 @@

    Classes

    class MessageListenerMatches -(keyword: Union[str, Pattern]) +(keyword: str | Pattern)

    A middleware can process request data before other middleware and listener functions.

    @@ -113,7 +113,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/middleware.html b/docs/static/api-docs/slack_bolt/middleware/middleware.html index 0d9d17f8b..c73ea7eba 100644 --- a/docs/static/api-docs/slack_bolt/middleware/middleware.html +++ b/docs/static/api-docs/slack_bolt/middleware/middleware.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.middleware API documentation - + @@ -121,7 +121,7 @@

    Instance variables

    Methods

    -def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) ‑> Optional[BoltResponse] +def process(self,
    *,
    req: BoltRequest,
    resp: BoltResponse,
    next: Callable[[], BoltResponse]) ‑> BoltResponse | None

    Processes a request data before other middleware and listeners. @@ -180,7 +180,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html b/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html index 6794d4927..8b32c0498 100644 --- a/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html +++ b/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.middleware_error_handler API documentation - + @@ -37,7 +37,7 @@

    Classes

    class CustomMiddlewareErrorHandler -(logger: logging.Logger, func: Callable[..., Optional[BoltResponse]]) +(logger: logging.Logger,
    func: Callable[..., BoltResponse | None])
    @@ -154,7 +154,7 @@

    Subclasses

    Methods

    -def handle(self, error: Exception, request: BoltRequest, response: Optional[BoltResponse]) ‑> None +def handle(self,
    error: Exception,
    request: BoltRequest,
    response: BoltResponse | None) ‑> None

    Handles an unhandled exception.

    @@ -203,7 +203,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/async_request_verification.html b/docs/static/api-docs/slack_bolt/middleware/request_verification/async_request_verification.html index 52ef2e164..88871be7e 100644 --- a/docs/static/api-docs/slack_bolt/middleware/request_verification/async_request_verification.html +++ b/docs/static/api-docs/slack_bolt/middleware/request_verification/async_request_verification.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.request_verification.async_request_verification API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncRequestVerification -(signing_secret: str, base_logger: Optional[logging.Logger] = None) +(signing_secret: str, base_logger: logging.Logger | None = None)

    Verifies an incoming request by checking the validity of @@ -131,7 +131,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html b/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html index bc08f3c88..f95218103 100644 --- a/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html +++ b/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.request_verification API documentation - + @@ -48,7 +48,7 @@

    Classes

    class RequestVerification -(signing_secret: str, base_logger: Optional[logging.Logger] = None) +(signing_secret: str, base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -165,7 +165,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html b/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html index 46e6c9f1b..691496c2d 100644 --- a/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html +++ b/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.request_verification.request_verification API documentation - + @@ -37,7 +37,7 @@

    Classes

    class RequestVerification -(signing_secret: str, base_logger: Optional[logging.Logger] = None) +(signing_secret: str, base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -148,7 +148,7 @@

    -

    Generated by pdoc 0.11.1.

    +

    Generated by pdoc 0.11.3.

    diff --git a/docs/static/api-docs/slack_bolt/middleware/ssl_check/async_ssl_check.html b/docs/static/api-docs/slack_bolt/middleware/ssl_check/async_ssl_check.html index 5a8e90b4d..4fea5ac08 100644 --- a/docs/static/api-docs/slack_bolt/middleware/ssl_check/async_ssl_check.html +++ b/docs/static/api-docs/slack_bolt/middleware/ssl_check/async_ssl_check.html @@ -3,13 +3,13 @@ - + slack_bolt.middleware.ssl_check.async_ssl_check API documentation - + @@ -37,7 +37,7 @@

    Classes

    class AsyncSslCheck -(verification_token: Optional[str] = None, base_logger: Optional[logging.Logger] = None) +(verification_token: str | None = None,
    base_logger: logging.Logger | None = None)

    A middleware can process request data before other middleware and listener functions.

    @@ -79,17 +79,6 @@

    Ancestors

  • Middleware
  • AsyncMiddleware
  • -

    Class variables

    -
    -
    var logger : logging.Logger
    -
    -
    -
    -
    var verification_token : Optional[str]
    -
    -
    -
    -

    Inherited members

  • +
    + +Expand source code + +
    async def to_aiohttp_response(bolt_resp: BoltResponse) -> web.Response:
    +    content_type = bolt_resp.headers.pop(
    +        "content-type",
    +        ["application/json" if bolt_resp.body.startswith("{") else "text/plain"],
    +    )[0]
    +    content_type = re.sub(r";\s*charset=utf-8", "", content_type)
    +    resp = web.Response(
    +        status=bolt_resp.status,
    +        body=bolt_resp.body,
    +        headers=bolt_resp.first_headers_without_set_cookie(),
    +        content_type=content_type,
    +    )
    +    for cookie in bolt_resp.cookies():
    +        for name, c in cookie.items():
    +            resp.set_cookie(
    +                name=name,
    +                value=c.value,
    +                max_age=c.get("max-age"),
    +                expires=c.get("expires"),
    +                path=c.get("path"),  # type: ignore[arg-type]
    +                domain=c.get("domain"),
    +                secure=True,
    +                httponly=True,
    +            )
    +    return resp
    +
    async def to_bolt_request(request: aiohttp.web_request.Request) ‑> AsyncBoltRequest
    +
    + +Expand source code + +
    async def to_bolt_request(request: web.Request) -> AsyncBoltRequest:
    +    return AsyncBoltRequest(
    +        body=await request.text(),
    +        query=request.query_string,
    +        headers=request.headers,  # type: ignore[arg-type]
    +    )
    +
    @@ -70,7 +122,7 @@

    Functions

    diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html b/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html index b36d86ed9..d598dc6cb 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html @@ -3,19 +3,30 @@ - + slack_bolt.adapter.asgi.aiohttp API documentation - + @@ -40,26 +51,6 @@

    Classes

    (app: AsyncApp,
    path: str = '/slack/events')
    -

    Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. -This can be used for production deployment.

    -

    With the default settings, http://localhost:3000/slack/events -Run Bolt with uvicron

    -
    # Python
    -app = AsyncApp()
    -api = SlackRequestHandler(app)
    -
    -# bash
    -export SLACK_SIGNING_SECRET=***
    -export SLACK_BOT_TOKEN=xoxb-***
    -uvicorn app:api --port 3000 --log-level debug
    -
    -

    Args

    -
    -
    app
    -
    Your bolt application
    -
    path
    -
    The path to handle request from Slack (Default: /slack/events)
    -
    Expand source code @@ -105,25 +96,40 @@

    Args

    AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) )
    +

    Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. +This can be used for production deployment.

    +

    With the default settings, http://localhost:3000/slack/events +Run Bolt with uvicron

    +
    # Python
    +app = AsyncApp()
    +api = SlackRequestHandler(app)
    +
    +# bash
    +export SLACK_SIGNING_SECRET=***
    +export SLACK_BOT_TOKEN=xoxb-***
    +uvicorn app:api --port 3000 --log-level debug
    +
    +

    Args

    +
    +
    app
    +
    Your bolt application
    +
    path
    +
    The path to handle request from Slack (Default: /slack/events)
    +

    Ancestors

    -

    Class variables

    -
    -
    var appAsyncApp
    -
    -
    -
    -

    Inherited members

    @@ -145,9 +151,6 @@

    Inherited members

    @@ -155,7 +158,7 @@

    diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html b/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html index 9bf506f09..9ecdb6fd3 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html @@ -3,19 +3,30 @@ - + slack_bolt.adapter.asgi.async_handler API documentation - + @@ -40,26 +51,6 @@

    Classes

    (app: AsyncApp,
    path: str = '/slack/events')
    -

    Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. -This can be used for production deployment.

    -

    With the default settings, http://localhost:3000/slack/events -Run Bolt with uvicron

    -
    # Python
    -app = AsyncApp()
    -api = SlackRequestHandler(app)
    -
    -# bash
    -export SLACK_SIGNING_SECRET=***
    -export SLACK_BOT_TOKEN=xoxb-***
    -uvicorn app:api --port 3000 --log-level debug
    -
    -

    Args

    -
    -
    app
    -
    Your bolt application
    -
    path
    -
    The path to handle request from Slack (Default: /slack/events)
    -
    Expand source code @@ -105,25 +96,40 @@

    Args

    AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) )
    +

    Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. +This can be used for production deployment.

    +

    With the default settings, http://localhost:3000/slack/events +Run Bolt with uvicron

    +
    # Python
    +app = AsyncApp()
    +api = SlackRequestHandler(app)
    +
    +# bash
    +export SLACK_SIGNING_SECRET=***
    +export SLACK_BOT_TOKEN=xoxb-***
    +uvicorn app:api --port 3000 --log-level debug
    +
    +

    Args

    +
    +
    app
    +
    Your bolt application
    +
    path
    +
    The path to handle request from Slack (Default: /slack/events)
    +

    Ancestors

    -

    Class variables

    -
    -
    var appAsyncApp
    -
    -
    -
    -

    Inherited members

    @@ -145,9 +151,6 @@

    Inherited members

    @@ -155,7 +158,7 @@

    -

    Generated by pdoc 0.11.3.

    +

    Generated by pdoc 0.11.5.

    diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html b/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html index 37deef2d1..2e194b12b 100644 --- a/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html +++ b/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html @@ -3,19 +3,30 @@ - + slack_bolt.adapter.asgi.base_handler API documentation - + @@ -39,7 +50,6 @@

    Classes

    class BaseSlackRequestHandler
    -
    Expand source code @@ -101,6 +111,7 @@

    Classes

    return raise TypeError(f"Unsupported scope type: {scope['type']!r}")
    +

    Subclasses

    @@ -280,7 +280,7 @@

    Static methods

    class WorkflowStepBuilder:
         """Steps from apps
    -    Refer to https://api.slack.com/workflows/steps for details.
    +    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.
         """
     
         callback_id: Union[str, Pattern]
    @@ -298,7 +298,7 @@ 

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps This builder is supposed to be used as decorator. @@ -340,7 +340,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Registers a new edit listener with details. @@ -394,7 +394,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Registers a new save listener with details. @@ -447,7 +447,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Registers a new execute listener with details. @@ -494,7 +494,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -584,10 +584,10 @@

    Static methods

    return _middleware

    Steps from apps -Refer to https://api.slack.com/workflows/steps for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    This builder is supposed to be used as decorator.

    my_step = WorkflowStep.builder("my_step")
     @my_step.edit
    @@ -703,7 +703,7 @@ 

    Methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -729,7 +729,7 @@

    Methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object.

    Returns

    @@ -753,7 +753,7 @@

    Returns

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Registers a new edit listener with details. @@ -799,7 +799,7 @@

    Returns

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    Registers a new edit listener with details.

    You can use this method as decorator as well.

    @my_step.edit
    @@ -844,7 +844,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Registers a new execute listener with details. @@ -889,7 +889,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    Registers a new execute listener with details.

    You can use this method as decorator as well.

    @my_step.execute
    @@ -934,7 +934,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps Registers a new save listener with details. @@ -979,7 +979,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    Registers a new save listener with details.

    You can use this method as decorator as well.

    @my_step.save
    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_complete.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_complete.html
    index 7dc7744a7..8a8790900 100644
    --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_complete.html
    +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_complete.html
    @@ -76,7 +76,7 @@ 

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepCompleted API method. - Refer to https://api.slack.com/methods/workflows.stepCompleted for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, client: AsyncWebClient, body: dict): @@ -108,7 +108,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepCompleted API method. -Refer to https://api.slack.com/methods/workflows.stepCompleted for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_configure.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_configure.html index 1e0395e92..3151e71bd 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_configure.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_configure.html @@ -83,7 +83,7 @@

    Classes

    ) app.step(ws) - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: AsyncWebClient, body: dict): @@ -131,7 +131,7 @@

    Classes

    ) app.step(ws)
    -

    Refer to https://api.slack.com/workflows/steps for details.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_fail.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_fail.html index 683174686..1206c45a6 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_fail.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_fail.html @@ -73,7 +73,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://api.slack.com/methods/workflows.stepFailed for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, client: AsyncWebClient, body: dict): @@ -106,7 +106,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://api.slack.com/methods/workflows.stepFailed for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_update.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_update.html index 8b8282fae..0d1cad162 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_update.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_update.html @@ -92,7 +92,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://api.slack.com/methods/workflows.updateStep for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, client: AsyncWebClient, body: dict): @@ -140,7 +140,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://api.slack.com/methods/workflows.updateStep for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/complete.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/complete.html index 685a3bd63..c15d51e9c 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/complete.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/complete.html @@ -76,7 +76,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepCompleted API method. - Refer to https://api.slack.com/methods/workflows.stepCompleted for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -108,7 +108,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepCompleted API method. -Refer to https://api.slack.com/methods/workflows.stepCompleted for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/configure.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/configure.html index 8199f9ea4..2c1aeadbf 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/configure.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/configure.html @@ -83,7 +83,7 @@

    Classes

    ) app.step(ws) - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: WebClient, body: dict): @@ -128,7 +128,7 @@

    Classes

    ) app.step(ws) -

    Refer to https://api.slack.com/workflows/steps for details.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/fail.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/fail.html index 071bbbfed..4c4091fd5 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/fail.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/fail.html @@ -73,7 +73,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://api.slack.com/methods/workflows.stepFailed for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -106,7 +106,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://api.slack.com/methods/workflows.stepFailed for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/update.html b/docs/static/api-docs/slack_bolt/workflows/step/utilities/update.html index f24c07836..c93fc7f21 100644 --- a/docs/static/api-docs/slack_bolt/workflows/step/utilities/update.html +++ b/docs/static/api-docs/slack_bolt/workflows/step/utilities/update.html @@ -92,7 +92,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://api.slack.com/methods/workflows.updateStep for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -140,7 +140,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://api.slack.com/methods/workflows.updateStep for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    From 1b3e3beadd9be0b925cf0c44c5746b82bc1713df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 09:44:28 -0400 Subject: [PATCH 104/282] chore(deps): bump http-proxy-middleware from 2.0.7 to 2.0.9 in /docs (#1299) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index e65be5252..ec9caf660 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8868,9 +8868,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", - "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.8", From f1c00487ae70eae7fc8ba21f057d78c56c724406 Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Wed, 30 Apr 2025 10:25:33 -0500 Subject: [PATCH 105/282] Docs: Fixed incorrect link (#1300) --- docs/content/tutorial/custom-steps-for-jira.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/tutorial/custom-steps-for-jira.md b/docs/content/tutorial/custom-steps-for-jira.md index d0fbe0979..b38f9337c 100644 --- a/docs/content/tutorial/custom-steps-for-jira.md +++ b/docs/content/tutorial/custom-steps-for-jira.md @@ -21,7 +21,7 @@ If you'd rather skip the tutorial and just head straight to the code, you can us 1. Navigate to the [app creation page](https://api.slack.com/apps/new) and select **From a manifest**. 2. Select the workspace you want to install the application in, then click **Next**. -3. Copy the contents of the [`manifest.json`](https://github.com/slack-samples/bolt-python-ai-chatbot/blob/main/manifest.json) file below into the text box that says **Paste your manifest code here** (within the **JSON** tab), then click **Next**: +3. Copy the contents of the [`manifest.json`](https://github.com/slack-samples/bolt-python-jira-functions/blob/main/manifest.json) file below into the text box that says **Paste your manifest code here** (within the **JSON** tab), then click **Next**: ```js reference title="manifest.json" https://github.com/slack-samples/bolt-python-jira-functions/blob/main/manifest.json From a750470e2f08b85a10f75eeaf277f61d295e6ba0 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 20 May 2025 18:27:31 -0700 Subject: [PATCH 106/282] ci: pin actions workflow step hashes and use minimum permissions (#1303) --- .github/workflows/codecov.yml | 13 +++++++++---- .github/workflows/docs-deploy.yml | 15 +++++++++------ .github/workflows/flake8.yml | 11 ++++++++--- .github/workflows/mypy.yml | 11 ++++++++--- .github/workflows/tests.yml | 13 +++++++++---- .github/workflows/triage-issues.yml | 15 +++++++-------- 6 files changed, 50 insertions(+), 28 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 79fd440b2..391c135c6 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -2,7 +2,8 @@ name: Run codecov on: push: - branches: [main] + branches: + - main pull_request: jobs: @@ -12,12 +13,16 @@ jobs: strategy: matrix: python-version: ["3.13"] + permissions: + contents: read env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -31,7 +36,7 @@ jobs: run: | pytest --cov=./slack_bolt/ --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 with: fail_ci_if_error: true verbose: true diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 54523819e..ed18c4b1d 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -5,23 +5,26 @@ on: branches: - main paths: - - 'docs/**' + - "docs/**" push: branches: - main paths: - - 'docs/**' + - "docs/**" workflow_dispatch: jobs: build: name: Build Docusaurus runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 20 cache: npm @@ -36,7 +39,7 @@ jobs: working-directory: ./docs - name: Upload Build Artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 with: path: ./docs/build @@ -59,4 +62,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 \ No newline at end of file + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index c64484b1b..87f3496e1 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -2,7 +2,8 @@ name: Run flake8 validation on: push: - branches: [main] + branches: + - main pull_request: jobs: @@ -12,10 +13,14 @@ jobs: strategy: matrix: python-version: ["3.13"] + permissions: + contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - name: Run flake8 verification diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index a592bd8cd..f333756b5 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -2,7 +2,8 @@ name: Run mypy validation on: push: - branches: [main] + branches: + - main pull_request: jobs: @@ -12,10 +13,14 @@ jobs: strategy: matrix: python-version: ["3.13"] + permissions: + contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - name: Run mypy verification diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bb35112c3..86fa4621c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,7 +2,8 @@ name: Run all the unit tests on: push: - branches: [main] + branches: + - main pull_request: jobs: @@ -20,10 +21,14 @@ jobs: - "3.11" - "3.12" - "3.13" + permissions: + contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - name: Install synchronous dependencies @@ -68,7 +73,7 @@ jobs: pytest tests/scenario_tests_async/ --junitxml=reports/test_scenario_async.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/test-results-action@v1 + uses: codecov/test-results-action@f2dba722c67b86c6caa034178c6e4d35335f6706 # v1.1.0 with: directory: ./reports/ flags: ${{ matrix.python-version }} diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index d1275a94d..b37c13422 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -4,20 +4,19 @@ name: Close stale issues and PRs -on: +on: workflow_dispatch: schedule: - - cron: '0 0 * * 1' - -permissions: - issues: write - pull-requests: write + - cron: "0 0 * * 1" jobs: stale: runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - - uses: actions/stale@v9.1.0 + - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 with: days-before-issue-stale: 30 days-before-issue-close: 10 @@ -30,4 +29,4 @@ jobs: exempt-all-milestones: true remove-stale-when-updated: true enable-statistics: true - operations-per-run: 60 \ No newline at end of file + operations-per-run: 60 From accf85d4a46650b6a51a6ad49538dbf160fc743d Mon Sep 17 00:00:00 2001 From: Tracy Rericha <108959677+technically-tracy@users.noreply.github.com> Date: Tue, 27 May 2025 12:09:34 -0400 Subject: [PATCH 107/282] Docs: moved over custom steps dynamic options page. (#1307) --- .../concepts/custom-steps-dynamic-options.md | 247 ++++++++++++++++++ docs/sidebars.js | 1 + 2 files changed, 248 insertions(+) create mode 100644 docs/content/concepts/custom-steps-dynamic-options.md diff --git a/docs/content/concepts/custom-steps-dynamic-options.md b/docs/content/concepts/custom-steps-dynamic-options.md new file mode 100644 index 000000000..cab3f7a61 --- /dev/null +++ b/docs/content/concepts/custom-steps-dynamic-options.md @@ -0,0 +1,247 @@ +# Custom Steps dynamic options for Workflow Builder + +## Background {#background} + +[Legacy steps from apps](https://docs.slack.dev/changelog/2023-08-workflow-steps-from-apps-step-back) previously enabled Slack apps to create and process custom workflow steps, which could then be shared and used by anyone in Workflow Builder. To support your transition away from them, custom steps used as dynamic options are available. These allow you to use data defined when referencing the step in Workflow Builder as inputs to the step. + +## Example use case {#use-case} + +Let's say a builder wants to add a custom step in Workflow Builder that creates an issue in an external issue-tracking system. First, they'll need to specify a project. Once a project is selected, a project-specific list of fields can be presented to them to choose from when creating the issue. + +As a developer, dynamic options allow you to supply data to input parameters of custom steps so that you can provide builders with varying sets of fields based on the builders' selections. + +In this example, the primary step would invoke a separate project selection step that retrieves the list of available projects. The builder-selected item from the retrieved list would then be used as the input to the secondary issue creation step. + +There are two parts necessary for Slack apps to support dynamic options: custom step definitions, and handling custom step dynamic options. We'll take a look at both in the following sections. + +## Custom step definitions {#custom-step-definitions} + +When defining an input to a custom step intended to be dynamic (rather than explicitly defining a set of input parameters up front), you'll define a `dynamic_options` property that points to another custom step designed to return the set of dynamic elements once this step is added to a workflow from Workflow Builder. + +An input parameter for a custom step can reference a different custom step that defines what data is available for it to return. One Slack app could even use another Slack app’s custom step to define dynamic options for one of its inputs. + +The following code snippet from our issue creation example discussed above shows a `create-issue` custom step that will be used as a workflow step. Another custom step, the `get-projects` step, will dynamically populate the project input parameter to be configured by a builder. This `get-projects` step provides an `array` containing projects fetched dynamically from the external issue-tracking system. + +```js + "functions": { + "create-issue": { + "title": "Create Issue", + "description": "", + "input_parameters": { + "support_channel": { + "type": "slack#/types/channel_id", + "title": "Support Channel", + "description": "", + "name": "support_channel" + }, + "project": { + "type": "string", + "title": "Project", + "description": "A project from the issue tracking system", + "is_required": true, + "dynamic_options": { + "function": "#/functions/get-projects", + "inputs": {} + } + }, + }, + "output_parameters": {} + }, + "get-projects": { + "title": "Get Projects", + "description": "Get the available project from the issue tracking system", + "input_parameters": {}, + "output_parameters": { + "options": { + "type": "slack#/types/options_select", + "title": "Project Options", + } + } + } + }, +``` +### Defining the `function` and `inputs` attributes {#define-attributes} + +Defining the `function` and `inputs` attributes of the `dynamic_options` property would look as follows: + +``` +"dynamic_options": { + "function": "#/functions/get-projects", + "inputs": {} +} +``` + +The `function` attribute specifies the step reference used to resolve the options of the input parameter. For example: `"#/functions/get-projects"`. + +The `inputs` attribute defines the parameters to be passed as inputs to the step referenced by the `function` attribute. For example: + +``` +"inputs": { + "selected_user_id": { + "value": "{{input_parameters.user_id}}" + }, + "query": { + "value": "{{client.query}}" + } +} +``` + +The following format can be used to reference any input parameter defined by the step: `{{input_parameters.}}`. + +In addition, the `{{client.query}}` parameter can be used as a placeholder for an input value. The `{{client.builder_context}}` parameter will inject the [`slack#/types/user_context`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types/#usercontext) of the user building the workflow as the value to the input parameter. + +### Types of dynamic options UIs {#dynamic-option-UIs} + +The above example demonstrates one possible UI to be rendered for builders: a single-select drop-down menu of dynamic options. However, dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. + +The type is dictated by the output parameter of the custom step used as a dynamic option. In order to use a custom step in a dynamic option context, its output must adhere to a defined interface, that is, it must have an `options` parameter of type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field), as shown in the following code snippet. + +```js +"output_parameters": { + "options": { + "type": "slack#/types/options_select" or "slack#/types/options_field", + "title": "Custom Options", + "description": "Options to be used in a dynamic context", + } + ... +} +``` + +#### Drop-down menus {#drop-down} + +Your dynamic input parameter can be rendered as a drop-down menu, which will use the options obtained from a custom step with an `options` output parameter of the type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select). + +The drop-down menu UI component can be rendered in two ways: single-select, or multi-select. To render the dynamic input as a single-select menu, the input parameter defining the dynamic option must be of the type [`string`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#string). + +```js +"step-with-dynamic-input": { + "title": "Step that uses a dynamic input", + "description": "This step uses a dynamic input rendered as a single-select menu", + "input_parameters": { + "dynamic_single_select": { + "type": "string", // this must be of type string for single-select + "title": "dynamic single select drop-down menu", + "description": "A dynamically-populated single-select drop-down menu", + "is_required": true, + "dynamic_options": { + "function": "#/functions/get-options", + "inputs": {}, + }, + } + }, + "output_parameters": {} +} +``` + +To render the dynamic input as a multi-select menu, the input parameter defining the dynamic option must be of the type [`array`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#array), and its `items` must be of type [`string`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#string). + +```js +"step-with-dynamic-input": { + "title": "Step that uses a dynamic input", + "description": "This step uses a dynamic input rendered as a multi-select menu", + "input_parameters": { + "dynamic_multi_select": { + "type": "array", // this must be of type array for multi-select + "items": { + "type": "string" + }, + "title": "dynamic single select drop-down menu", + "description": "A dynamically-populated multi-select drop-down menu", + "dynamic_options": { + "function": "#/functions/get-options", + "inputs": {}, + }, + } + }, + "output_parameters": {} +} +``` + +#### Fields {#fields} + +In the code snippet below, the input parameter is rendered as a set of fields with keys and values. The option fields are obtained from a custom step with an `options` output parameter of type [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field). + +The input parameter that defines the dynamic option must be of type [`object`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#object), as the completed set of fields in Workflow Builder will be passed to the custom step as an [untyped object](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#untyped-object) during workflow execution. + +```js +"test-field-dynamic-options": { + "title": "Test dynamic field options", + "description": "", + "input_parameters": { + "dynamic_fields": { + "type": "object", + "title": "Dynamic custom field options", + "description": "A dynamically-populated section of input fields", + "dynamic_options": { + "function": "#/functions/get-field-options", + "inputs": {} + "selection_type": "key-value", + } + } + }, + "output_parameters": {} +} +``` + +### Dynamic option types {#dynamic-option-types} + +As mentioned earlier, in order to use a custom step as a dynamic option, its output must adhere to a defined interface: it must have an `options` output parameter of the type either [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field). + +To take a look at these in more detail, refer to our [Options Slack type](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options) documentation. + +## Dynamic options handler {#dynamic-option-handler} + +Each custom step defined in the manifest needs a corresponding handler in your Slack app. Although implemented similarly to existing function execution event handlers, there are two key differences between regular custom step invocations and those used for dynamic options: + +* The custom step must have an `options` output parameter that is of type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field). +* The [`function_executed`](https://docs.slack.dev/reference/events/function_executed) event must be handled synchronously. This optimizes the response time of returned dynamic options and provides a crisp builder experience. + +### Asynchronous event handling {#async} + +By default, the [Bolt family of frameworks](https://tools.slack.dev/) handles `function_executed` events asynchronously. + +For example, the various modal-related API methods provide two ways to update a view: synchronously using a `response_action` HTTP response, or asynchronously using a separate HTTP API call. Using the asynchronous approach allows developers to handle events free of timeouts, but this isn't desired for dynamic options as it introduces delays and violates our stated goal of providing a crisp builder experience. + +### Synchronous event handling {#sync} + +Dynamic options support synchronous handling of `function_executed` events. By ensuring that the function execution’s state is complete with output parameters provided before responding to the `function_executed` event, Slack can quickly provide Workflow Builder with the requisite dynamic options. + +### Implementation {#implementation} + +To optimize the response time of dynamic options, you must acknowledge the incoming event after calling the [`function.completeSuccess`](https://docs.slack.dev/reference/methods/functions.completeSuccess) or [`function.completeError`](https://docs.slack.dev/reference/methods/functions.completeError) API methods, minimizing asynchronous latency. The `function.completeSuccess` and `function.completeError` API methods are invoked in the complete and fail helper functions. ([For example](https://github.com/slackapi/bolt-python?tab=readme-ov-file#making-things-happen)). + +A new `auto_acknowledge` flag allows you more granular control over whether specific event handlers should operate in synchronous or asynchronous response modes in order to enable a smooth dynamic options experience. + +#### Example {#bolt-py} + +In [Bolt for Python](https://tools.slack.dev/bolt-python/), you can set `auto_acknowledge=False` on a specific function decorator. This allows you to manually control when the `ack()` event acknowledgement helper function is executed. It flips Bolt to synchronous `function_executed` event handling mode for the specific handler. + +```py +@app.function("get-projects", auto_acknowledge=False) +def handle_get_projects(ack: Ack, complete: Complete): + try: + complete( + outputs={ + "options": [ + { + "text": { + "type": "plain_text", + "text": "Secret Squirrel Project", + }, + "value": "p1", + }, + { + "text": { + "type": "plain_text", + "text": "Public Kangaroo Project", + }, + "value": "p2", + }, + ] + } + ) + finally: + ack() +``` + +✨ **To learn more about the Bolt family of frameworks and tools**, check out our [Slack Developer Tools](https://tools.slack.dev/). diff --git a/docs/sidebars.js b/docs/sidebars.js index 752b61787..82209d428 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -41,6 +41,7 @@ const sidebars = { }, "concepts/ai-apps", "concepts/custom-steps", + "concepts/custom-steps-dynamic-options", { type: "category", label: "App Configuration", From dd39ed5350fb4dc3984fb7f8d9e36f321153b8dc Mon Sep 17 00:00:00 2001 From: Tracy Rericha <108959677+technically-tracy@users.noreply.github.com> Date: Tue, 27 May 2025 12:36:17 -0400 Subject: [PATCH 108/282] Docs: Updated links to match Bolt JS. (#1308) --- docs/content/tutorial/custom-steps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/tutorial/custom-steps.md b/docs/content/tutorial/custom-steps.md index 79b02089b..2486a49ef 100644 --- a/docs/content/tutorial/custom-steps.md +++ b/docs/content/tutorial/custom-steps.md @@ -69,7 +69,7 @@ Field | Type | Description `type` | String | Defines the data type and can fall into one of two categories: primitives or Slack-specific. `title` | String | The label that appears in Workflow Builder when a user sets up this step in their workflow. `description` | String | The description that accompanies the input when a user sets up this step in their workflow. -`dynamic_options` | Object | For custom steps dynamic options in Workflow Builder, define this property and point to a custom step designed to return the set of dynamic elements once the step is added to a workflow within Workflow Builder. Dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. Refer to [custom steps dynamic options in Workflow Builder](/automation/runonslack/custom-steps-dynamic-options) for more details. +`dynamic_options` | Object | For custom steps dynamic options in Workflow Builder, define this property and point to a custom step designed to return the set of dynamic elements once the step is added to a workflow within Workflow Builder. Dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. Refer to custom steps dynamic options for Workflow Builder using [Bolt for JavaScript](https://tools.slack.dev/bolt-js/concepts/custom-steps-dynamic-options/) or [Bolt for Python](https://tools.slack.dev/bolt-python/concepts/custom-steps-dynamic-options/) for more details. `is_required` | Boolean | Indicates whether or not the input is required by the step in order to run. If it’s required and not provided, the user will not be able to save the configuration nor use the step in their workflow. This property is available only in v1 of the manifest. We recommend v2, using the `required` array as noted in the example above. `hint` | String | Helper text that appears below the input when a user sets up this step in their workflow. From e2896fdff75bf65fd8d729cdfe6180e33ecf092c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 18:51:59 -0700 Subject: [PATCH 109/282] chore(deps): bump codecov/test-results-action from 1.1.0 to 1.1.1 (#1310) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 86fa4621c..a1135f265 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -73,7 +73,7 @@ jobs: pytest tests/scenario_tests_async/ --junitxml=reports/test_scenario_async.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/test-results-action@f2dba722c67b86c6caa034178c6e4d35335f6706 # v1.1.0 + uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1 with: directory: ./reports/ flags: ${{ matrix.python-version }} From 79fb18add72b40599dbb1322c41ddf2812c4b2e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 18:55:58 -0700 Subject: [PATCH 110/282] chore(deps): bump the docusaurus group in /docs with 5 updates (#1311) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Brooks --- docs/package-lock.json | 1990 ++++++++++++++++------------------------ docs/package.json | 10 +- 2 files changed, 778 insertions(+), 1222 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index ec9caf660..303f5487a 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,9 +8,9 @@ "name": "website", "version": "2024.08.01", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/plugin-client-redirects": "^3.7.0", - "@docusaurus/preset-classic": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/plugin-client-redirects": "^3.8.0", + "@docusaurus/preset-classic": "3.8.0", "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -19,8 +19,8 @@ "react-dom": "^19.1.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/types": "3.7.0" + "@docusaurus/module-type-aliases": "3.8.0", + "@docusaurus/types": "3.8.0" }, "engines": { "node": ">=20.0" @@ -72,99 +72,99 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.20.4.tgz", - "integrity": "sha512-OZ3Xvvf+k7NMcwmmioIVX+76E/KKtN607NCMNsBEKe+uHqktZ+I5bmi/EVr2m5VF59Gnh9MTlJCdXtBiGjruxw==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.25.0.tgz", + "integrity": "sha512-1pfQulNUYNf1Tk/svbfjfkLBS36zsuph6m+B6gDkPEivFmso/XnRgwDvjAx80WNtiHnmeNjIXdF7Gos8+OLHqQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.20.4.tgz", - "integrity": "sha512-8pM5zQpHonCIBxKmMyBLgQoaSKUNBE5u741VEIjn2ArujolhoKRXempRAlLwEg5hrORKl9XIlit00ff4g6LWvA==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.25.0.tgz", + "integrity": "sha512-AFbG6VDJX/o2vDd9hqncj1B6B4Tulk61mY0pzTtzKClyTDlNP0xaUiEKhl6E7KO9I/x0FJF5tDCm0Hn6v5x18A==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.20.4.tgz", - "integrity": "sha512-OCGa8hKAP6kQKBwi+tu9flTXshz4qeCK5P8J6bI1qq8KYs+/TU1xSotT+E7hO+uyDanGU6dT6soiMSi4A38JgA==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.25.0.tgz", + "integrity": "sha512-il1zS/+Rc6la6RaCdSZ2YbJnkQC6W1wiBO8+SH+DE6CPMWBU6iDVzH0sCKSAtMWl9WBxoN6MhNjGBnCv9Yy2bA==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.20.4.tgz", - "integrity": "sha512-MroyJStJFLf/cYeCbguCRdrA2U6miDVqbi3t9ZGovBWWTef7BZwVQG0mLyInzp4MIjBfwqu3xTrhxsiiOavX3A==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.25.0.tgz", + "integrity": "sha512-blbjrUH1siZNfyCGeq0iLQu00w3a4fBXm0WRIM0V8alcAPo7rWjLbMJMrfBtzL9X5ic6wgxVpDADXduGtdrnkw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.20.4.tgz", - "integrity": "sha512-bVR5sxFfgCQ+G0ZegGVhBqtaDd7jCfr33m5mGuT43U+bH//xeqAHQyIS4abcmRulwqeIAHNm5Yl2J7grT3z//A==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.25.0.tgz", + "integrity": "sha512-aywoEuu1NxChBcHZ1pWaat0Plw7A8jDMwjgRJ00Mcl7wGlwuPt5dJ/LTNcg3McsEUbs2MBNmw0ignXBw9Tbgow==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.20.4.tgz", - "integrity": "sha512-ZHsV0vceNDR87wIVaz7VjxilwCUCkzbuy4QnqIdnQs3NnC43is7KKbEtKueuNw+YGMdx+wmD5kRI2XKip1R93A==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.25.0.tgz", + "integrity": "sha512-a/W2z6XWKjKjIW1QQQV8PTTj1TXtaKx79uR3NGBdBdGvVdt24KzGAaN7sCr5oP8DW4D3cJt44wp2OY/fZcPAVA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.20.4.tgz", - "integrity": "sha512-hXM2LpwTzG5kGQSyq3feIijzzl6vkjYPP+LF3ru1relNUIh7fWJ4uYQay2NMNbWX5LWQzF8Vr9qlIA139doQXg==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.25.0.tgz", + "integrity": "sha512-9rUYcMIBOrCtYiLX49djyzxqdK9Dya/6Z/8sebPn94BekT+KLOpaZCuc6s0Fpfq7nx5J6YY5LIVFQrtioK9u0g==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" @@ -177,81 +177,81 @@ "license": "MIT" }, "node_modules/@algolia/ingestion": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.20.4.tgz", - "integrity": "sha512-idAe53XsTlLSSQ7pJcjscUEmc67vEM+VohYkr78Ebfb43vtfKH0ik8ux9OGQpLRNGntaHqpe/lfU5PDRi5/92w==", + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.25.0.tgz", + "integrity": "sha512-jJeH/Hk+k17Vkokf02lkfYE4A+EJX+UgnMhTLR/Mb+d1ya5WhE+po8p5a/Nxb6lo9OLCRl6w3Hmk1TX1e9gVbQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.20.4.tgz", - "integrity": "sha512-O6HjdSWtyu5LhHR7gdU83oWbl1vVVRwoTxkENHF61Ar7l9C1Ok91VtnK7RtXB9pJL1kpIMDExwZOT5sEN2Ppfw==", + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.25.0.tgz", + "integrity": "sha512-Ls3i1AehJ0C6xaHe7kK9vPmzImOn5zBg7Kzj8tRYIcmCWVyuuFwCIsbuIIz/qzUf1FPSWmw0TZrGeTumk2fqXg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.20.4.tgz", - "integrity": "sha512-p8M78pQjPrN6PudO2TnkWiOJbyp/IPhgCFBW8aZrLshhZpPkV9N4u0YsU/w6OoeYDKSxmXntWQrKYiU1dVRWfg==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.25.0.tgz", + "integrity": "sha512-79sMdHpiRLXVxSjgw7Pt4R1aNUHxFLHiaTDnN2MQjHwJ1+o3wSseb55T9VXU4kqy3m7TUme3pyRhLk5ip/S4Mw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "@algolia/client-common": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.20.4.tgz", - "integrity": "sha512-Y8GThjDVdhFUurZKKDdzAML/LNKOA/BOydEcaFeb/g4Iv4Iq0qQJs6aIbtdsngUU6cu74qH/2P84kr2h16uVvQ==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.25.0.tgz", + "integrity": "sha512-JLaF23p1SOPBmfEqozUAgKHQrGl3z/Z5RHbggBu6s07QqXXcazEsub5VLonCxGVqTv6a61AAPr8J1G5HgGGjEw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4" + "@algolia/client-common": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.20.4.tgz", - "integrity": "sha512-OrAUSrvbFi46U7AxOXkyl9QQiaW21XWpixWmcx3D2S65P/DCIGOVE6K2741ZE+WiKIqp+RSYkyDFj3BiFHzLTg==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.25.0.tgz", + "integrity": "sha512-rtzXwqzFi1edkOF6sXxq+HhmRKDy7tz84u0o5t1fXwz0cwx+cjpmxu/6OQKTdOJFS92JUYHsG51Iunie7xbqfQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4" + "@algolia/client-common": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.20.4.tgz", - "integrity": "sha512-Jc/bofGBw4P9nBii4oCzCqqusv8DAFFORfUD2Ce1cZk3fvUPk+q/Qnu7i9JpTSHjMc0MWzqApLdq7Nwh1gelLg==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.25.0.tgz", + "integrity": "sha512-ZO0UKvDyEFvyeJQX0gmZDQEvhLZ2X10K+ps6hViMo1HgE2V8em00SwNsQ+7E/52a+YiBkVWX61pJJJE44juDMQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.20.4" + "@algolia/client-common": "5.25.0" }, "engines": { "node": ">= 14.0.0" @@ -270,13 +270,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -328,12 +329,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz", + "integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.27.3", + "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -441,9 +443,10 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", + "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -468,12 +471,13 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -507,9 +511,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -572,17 +576,19 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -622,12 +628,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", + "integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.10" + "@babel/types": "^7.27.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -1370,12 +1376,12 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", - "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.27.1.tgz", + "integrity": "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1490,15 +1496,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.9.tgz", - "integrity": "sha512-Jf+8y9wXQbbxvVYTM8gO5oEF2POdNji0NMltEkG7FtmzD9PVz7/lxpqSdTvwsjTMU5HIHuDVNf2SOxLkWi+wPQ==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.4.tgz", + "integrity": "sha512-D68nR5zxU64EUzV8i7T3R5XP0Xhrou/amNnddsRQssx6GrTLdZl1rLxyjtVZBd+v/NVX4AbTPOB5aU8thAZV1A==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -1509,6 +1515,19 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1819,42 +1838,42 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.10.tgz", - "integrity": "sha512-uITFQYO68pMEYR46AHgQoyBg7KPPJDAbGn4jUTIRgCFJIp88MIBUianVOplhZDEec07bp9zIyr4Kp0FCyQzmWg==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.4.tgz", + "integrity": "sha512-H7QhL0ucCGOObsUETNbB2PuzF4gAvN8p32P6r91bX7M/hk4bx+3yz2hTwHL9d/Efzwu1upeb4/cd7oSxCzup3w==", "license": "MIT", "dependencies": { - "core-js-pure": "^3.30.2", - "regenerator-runtime": "^0.14.0" + "core-js-pure": "^3.30.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1863,13 +1882,13 @@ } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1886,9 +1905,9 @@ } }, "node_modules/@csstools/cascade-layer-name-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.4.tgz", - "integrity": "sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", + "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", "funding": [ { "type": "github", @@ -1904,8 +1923,8 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/color-helpers": { @@ -1928,9 +1947,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", - "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", "funding": [ { "type": "github", @@ -1946,14 +1965,14 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", - "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", "funding": [ { "type": "github", @@ -1967,20 +1986,20 @@ "license": "MIT", "dependencies": { "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.2" + "@csstools/css-calc": "^2.1.4" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", "funding": [ { "type": "github", @@ -1996,13 +2015,13 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", "funding": [ { "type": "github", @@ -2019,9 +2038,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", - "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", "funding": [ { "type": "github", @@ -2037,8 +2056,8 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/postcss-cascade-layers": { @@ -2103,9 +2122,9 @@ } }, "node_modules/@csstools/postcss-color-function": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.8.tgz", - "integrity": "sha512-9dUvP2qpZI6PlGQ/sob+95B3u5u7nkYt9yhZFCC7G9HBRHBxj+QxS/wUlwaMGYW0waf+NIierI8aoDTssEdRYw==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.10.tgz", + "integrity": "sha512-4dY0NBu7NVIpzxZRgh/Q/0GPSz/jLSw0i/u3LTUor0BkQcz/fNhN10mSWBDsL0p9nDb0Ky1PD6/dcGbhACuFTQ==", "funding": [ { "type": "github", @@ -2118,10 +2137,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2132,9 +2151,38 @@ } }, "node_modules/@csstools/postcss-color-mix-function": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.8.tgz", - "integrity": "sha512-yuZpgWUzqZWQhEqfvtJufhl28DgO9sBwSbXbf/59gejNuvZcoUTRGQZhzhwF4ccqb53YAGB+u92z9+eSKoB4YA==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.10.tgz", + "integrity": "sha512-P0lIbQW9I4ShE7uBgZRib/lMTf9XMjJkFl/d6w4EMNHu2qvQ6zljJGEcBkw/NsBtq/6q3WrmgxSS8kHtPMkK4Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.0.tgz", + "integrity": "sha512-Z5WhouTyD74dPFPrVE7KydgNS9VvnjB8qcdes9ARpCOItb4jTnm7cHp4FhxCRUoyhabD0WVv43wbkJ4p8hLAlQ==", "funding": [ { "type": "github", @@ -2147,10 +2195,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2161,9 +2209,9 @@ } }, "node_modules/@csstools/postcss-content-alt-text": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.4.tgz", - "integrity": "sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.6.tgz", + "integrity": "sha512-eRjLbOjblXq+byyaedQRSrAejKGNAFued+LcbzT+LCL78fabxHkxYjBbxkroONxHHYu2qxhFK2dBStTLPG3jpQ==", "funding": [ { "type": "github", @@ -2176,9 +2224,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2189,9 +2237,9 @@ } }, "node_modules/@csstools/postcss-exponential-functions": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.7.tgz", - "integrity": "sha512-XTb6Mw0v2qXtQYRW9d9duAjDnoTbBpsngD7sRNLmYDjvwU2ebpIHplyxgOeo6jp/Kr52gkLi5VaK5RDCqzMzZQ==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz", + "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==", "funding": [ { "type": "github", @@ -2204,9 +2252,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2242,9 +2290,9 @@ } }, "node_modules/@csstools/postcss-gamut-mapping": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.8.tgz", - "integrity": "sha512-/K8u9ZyGMGPjmwCSIjgaOLKfic2RIGdFHHes84XW5LnmrvdhOTVxo255NppHi3ROEvoHPW7MplMJgjZK5Q+TxA==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.10.tgz", + "integrity": "sha512-QDGqhJlvFnDlaPAfCYPsnwVA6ze+8hhrwevYWlnUeSjkkZfBpcCO42SaUD8jiLlq7niouyLgvup5lh+f1qessg==", "funding": [ { "type": "github", @@ -2257,9 +2305,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2269,9 +2317,9 @@ } }, "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.8.tgz", - "integrity": "sha512-CoHQ/0UXrvxLovu0ZeW6c3/20hjJ/QRg6lyXm3dZLY/JgvRU6bdbQZF/Du30A4TvowfcgvIHQmP1bNXUxgDrAw==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.10.tgz", + "integrity": "sha512-HHPauB2k7Oits02tKFUeVFEU2ox/H3OQVrP3fSOKDxvloOikSal+3dzlyTZmYsb9FlY9p5EUpBtz0//XBmy+aw==", "funding": [ { "type": "github", @@ -2284,10 +2332,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2298,9 +2346,9 @@ } }, "node_modules/@csstools/postcss-hwb-function": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.8.tgz", - "integrity": "sha512-LpFKjX6hblpeqyych1cKmk+3FJZ19QmaJtqincySoMkbkG/w2tfbnO5oE6mlnCTXcGUJ0rCEuRHvTqKK0nHYUQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.10.tgz", + "integrity": "sha512-nOKKfp14SWcdEQ++S9/4TgRKchooLZL0TUFdun3nI4KPwCjETmhjta1QT4ICQcGVWQTvrsgMM/aLB5We+kMHhQ==", "funding": [ { "type": "github", @@ -2313,10 +2361,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2327,9 +2375,9 @@ } }, "node_modules/@csstools/postcss-ic-unit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.0.tgz", - "integrity": "sha512-9QT5TDGgx7wD3EEMN3BSUG6ckb6Eh5gSPT5kZoVtUuAonfPmLDJyPhqR4ntPpMYhUKAMVKAg3I/AgzqHMSeLhA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.2.tgz", + "integrity": "sha512-lrK2jjyZwh7DbxaNnIUjkeDmU8Y6KyzRBk91ZkI5h8nb1ykEfZrtIVArdIjX4DHMIBGpdHrgP0n4qXDr7OHaKA==", "funding": [ { "type": "github", @@ -2342,7 +2390,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -2437,9 +2485,9 @@ } }, "node_modules/@csstools/postcss-light-dark-function": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.7.tgz", - "integrity": "sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.9.tgz", + "integrity": "sha512-1tCZH5bla0EAkFAI2r0H33CDnIBeLUaJh1p+hvvsylJ4svsv2wOmJjJn+OXwUZLXef37GYbRIVKX+X+g6m+3CQ==", "funding": [ { "type": "github", @@ -2452,9 +2500,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2556,9 +2604,9 @@ } }, "node_modules/@csstools/postcss-logical-viewport-units": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.3.tgz", - "integrity": "sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz", + "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==", "funding": [ { "type": "github", @@ -2571,7 +2619,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/css-tokenizer": "^3.0.4", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2582,9 +2630,9 @@ } }, "node_modules/@csstools/postcss-media-minmax": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.7.tgz", - "integrity": "sha512-LB6tIP7iBZb5CYv8iRenfBZmbaG3DWNEziOnPjGoQX5P94FBPvvTBy68b/d9NnS5PELKwFmmOYsAEIgEhDPCHA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz", + "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==", "funding": [ { "type": "github", @@ -2597,10 +2645,10 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -2610,9 +2658,9 @@ } }, "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.4.tgz", - "integrity": "sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz", + "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==", "funding": [ { "type": "github", @@ -2625,9 +2673,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -2688,9 +2736,9 @@ } }, "node_modules/@csstools/postcss-oklab-function": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.8.tgz", - "integrity": "sha512-+5aPsNWgxohXoYNS1f+Ys0x3Qnfehgygv3qrPyv+Y25G0yX54/WlVB+IXprqBLOXHM1gsVF+QQSjlArhygna0Q==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.10.tgz", + "integrity": "sha512-ZzZUTDd0fgNdhv8UUjGCtObPD8LYxMH+MJsW9xlZaWTV8Ppr4PtxlHYNMmF4vVWGl0T6f8tyWAKjoI6vePSgAg==", "funding": [ { "type": "github", @@ -2703,10 +2751,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2717,9 +2765,9 @@ } }, "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz", - "integrity": "sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.1.0.tgz", + "integrity": "sha512-YrkI9dx8U4R8Sz2EJaoeD9fI7s7kmeEBfmO+UURNeL6lQI7VxF6sBE+rSqdCBn4onwqmxFdBU3lTwyYb/lCmxA==", "funding": [ { "type": "github", @@ -2742,9 +2790,9 @@ } }, "node_modules/@csstools/postcss-random-function": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-1.0.3.tgz", - "integrity": "sha512-dbNeEEPHxAwfQJ3duRL5IPpuD77QAHtRl4bAHRs0vOVhVbHrsL7mHnwe0irYjbs9kYwhAHZBQTLBgmvufPuRkA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", + "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==", "funding": [ { "type": "github", @@ -2757,9 +2805,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2769,9 +2817,9 @@ } }, "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.8.tgz", - "integrity": "sha512-eGE31oLnJDoUysDdjS9MLxNZdtqqSxjDXMdISpLh80QMaYrKs7VINpid34tWQ+iU23Wg5x76qAzf1Q/SLLbZVg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.10.tgz", + "integrity": "sha512-8+0kQbQGg9yYG8hv0dtEpOMLwB9M+P7PhacgIzVzJpixxV4Eq9AUQtQw8adMmAJU1RBBmIlpmtmm3XTRd/T00g==", "funding": [ { "type": "github", @@ -2784,10 +2832,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -2836,9 +2884,9 @@ } }, "node_modules/@csstools/postcss-sign-functions": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.2.tgz", - "integrity": "sha512-4EcAvXTUPh7n6UoZZkCzgtCf/wPzMlTNuddcKg7HG8ozfQkUcHsJ2faQKeLmjyKdYPyOUn4YA7yDPf8K/jfIxw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz", + "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==", "funding": [ { "type": "github", @@ -2851,9 +2899,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2863,9 +2911,9 @@ } }, "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.7.tgz", - "integrity": "sha512-rdrRCKRnWtj5FyRin0u/gLla7CIvZRw/zMGI1fVJP0Sg/m1WGicjPVHRANL++3HQtsiXKAbPrcPr+VkyGck0IA==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz", + "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==", "funding": [ { "type": "github", @@ -2878,9 +2926,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -2916,9 +2964,9 @@ } }, "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.7.tgz", - "integrity": "sha512-qTrZgLju3AV7Djhzuh2Bq/wjFqbcypnk0FhHjxW8DWJQcZLS1HecIus4X2/RLch1ukX7b+YYCdqbEnpIQO5ccg==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", + "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", "funding": [ { "type": "github", @@ -2931,9 +2979,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -3034,9 +3082,9 @@ } }, "node_modules/@docusaurus/babel": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.7.0.tgz", - "integrity": "sha512-0H5uoJLm14S/oKV3Keihxvh8RV+vrid+6Gv+2qhuzbqHanawga8tYnsdpjEyt36ucJjqlby2/Md2ObWjA02UXQ==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.0.tgz", + "integrity": "sha512-9EJwSgS6TgB8IzGk1L8XddJLhZod8fXT4ULYMx6SKqyCBqCFpVCEjR/hNXXhnmtVM2irDuzYoVLGWv7srG/VOA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", @@ -3049,8 +3097,8 @@ "@babel/runtime": "^7.25.9", "@babel/runtime-corejs3": "^7.25.9", "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.7.0", - "@docusaurus/utils": "3.7.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/utils": "3.8.0", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3060,17 +3108,17 @@ } }, "node_modules/@docusaurus/bundler": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.7.0.tgz", - "integrity": "sha512-CUUT9VlSGukrCU5ctZucykvgCISivct+cby28wJwCC/fkQFgAHRp/GKv2tx38ZmXb7nacrKzFTcp++f9txUYGg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.0.tgz", + "integrity": "sha512-Rq4Z/MSeAHjVzBLirLeMcjLIAQy92pF1OI+2rmt18fSlMARfTGLWRE8Vb+ljQPTOSfJxwDYSzsK6i7XloD2rNA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.7.0", - "@docusaurus/cssnano-preset": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", + "@docusaurus/babel": "3.8.0", + "@docusaurus/cssnano-preset": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", "babel-loader": "^9.2.1", "clean-css": "^5.3.2", "copy-webpack-plugin": "^11.0.0", @@ -3084,7 +3132,6 @@ "postcss": "^8.4.26", "postcss-loader": "^7.3.3", "postcss-preset-env": "^10.1.0", - "react-dev-utils": "^12.0.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "url-loader": "^4.1.1", @@ -3104,18 +3151,18 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.7.0.tgz", - "integrity": "sha512-b0fUmaL+JbzDIQaamzpAFpTviiaU4cX3Qz8cuo14+HGBCwa0evEK0UYCBFY3n4cLzL8Op1BueeroUD2LYAIHbQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/babel": "3.7.0", - "@docusaurus/bundler": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.0.tgz", + "integrity": "sha512-c7u6zFELmSGPEP9WSubhVDjgnpiHgDqMh1qVdCB7rTflh4Jx0msTYmMiO91Ez0KtHj4sIsDsASnjwfJ2IZp3Vw==", + "license": "MIT", + "dependencies": { + "@docusaurus/babel": "3.8.0", + "@docusaurus/bundler": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/mdx-loader": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -3123,19 +3170,19 @@ "combine-promises": "^1.1.0", "commander": "^5.1.0", "core-js": "^3.31.1", - "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", "eval": "^0.1.8", + "execa": "5.1.1", "fs-extra": "^11.1.1", "html-tags": "^3.3.1", "html-webpack-plugin": "^5.6.0", "leven": "^3.1.0", "lodash": "^4.17.21", + "open": "^8.4.0", "p-map": "^4.0.0", "prompts": "^2.4.2", - "react-dev-utils": "^12.0.1", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", @@ -3144,7 +3191,7 @@ "react-router-dom": "^5.3.4", "semver": "^7.5.4", "serve-handler": "^6.1.6", - "shelljs": "^0.8.5", + "tinypool": "^1.0.2", "tslib": "^2.6.0", "update-notifier": "^6.0.2", "webpack": "^5.95.0", @@ -3165,9 +3212,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.7.0.tgz", - "integrity": "sha512-X9GYgruZBSOozg4w4dzv9uOz8oK/EpPVQXkp0MM6Tsgp/nRIU9hJzJ0Pxg1aRa3xCeEQTOimZHcocQFlLwYajQ==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.0.tgz", + "integrity": "sha512-UJ4hAS2T0R4WNy+phwVff2Q0L5+RXW9cwlH6AEphHR5qw3m/yacfWcSK7ort2pMMbDn8uGrD38BTm4oLkuuNoQ==", "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", @@ -3180,9 +3227,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.7.0.tgz", - "integrity": "sha512-z7g62X7bYxCYmeNNuO9jmzxLQG95q9QxINCwpboVcNff3SJiHJbGrarxxOVMVmAh1MsrSfxWkVGv4P41ktnFsA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.0.tgz", + "integrity": "sha512-7eEMaFIam5Q+v8XwGqF/n0ZoCld4hV4eCCgQkfcN9Mq5inoZa6PHHW9Wu6lmgzoK5Kx3keEeABcO2SxwraoPDQ==", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -3193,21 +3240,21 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.7.0.tgz", - "integrity": "sha512-OFBG6oMjZzc78/U3WNPSHs2W9ZJ723ewAcvVJaqS0VgyeUfmzUV8f1sv+iUHA0DtwiR5T5FjOxj6nzEE8LY6VA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.0.tgz", + "integrity": "sha512-mDPSzssRnpjSdCGuv7z2EIAnPS1MHuZGTaRLwPn4oQwszu4afjWZ/60sfKjTnjBjI8Vl4OgJl2vMmfmiNDX4Ng==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", "estree-util-value-to-estree": "^3.0.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", - "image-size": "^1.0.2", + "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-string": "^4.0.0", "rehype-raw": "^7.0.0", @@ -3232,17 +3279,17 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.7.0.tgz", - "integrity": "sha512-g7WdPqDNaqA60CmBrr0cORTrsOit77hbsTj7xE2l71YhBn79sxdm7WMK7wfhcaafkbpIh7jv5ef5TOpf1Xv9Lg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.0.tgz", + "integrity": "sha512-/uMb4Ipt5J/QnD13MpnoC/A4EYAe6DKNWqTWLlGrqsPJwJv73vSwkA25xnYunwfqWk0FlUQfGv/Swdh5eCCg7g==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.7.0", + "@docusaurus/types": "3.8.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", - "react-helmet-async": "npm:@slorber/react-helmet-async@*", + "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" }, "peerDependencies": { @@ -3251,16 +3298,16 @@ } }, "node_modules/@docusaurus/plugin-client-redirects": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.7.0.tgz", - "integrity": "sha512-6B4XAtE5ZVKOyhPgpgMkb7LwCkN+Hgd4vOnlbwR8nCdTQhLjz8MHbGlwwvZ/cay2SPNRX5KssqKAlcHVZP2m8g==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.8.0.tgz", + "integrity": "sha512-J8f5qzAlO61BnG1I91+N5WH1b/lPWqn6ifTxf/Bluz9JVe1bhFNSl0yW03p+Ff3AFOINDy2ofX70al9nOnOLyw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "eta": "^2.2.0", "fs-extra": "^11.1.1", "lodash": "^4.17.21", @@ -3275,24 +3322,24 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.7.0.tgz", - "integrity": "sha512-EFLgEz6tGHYWdPU0rK8tSscZwx+AsyuBW/r+tNig2kbccHYGUJmZtYN38GjAa3Fda4NU+6wqUO5kTXQSRBQD3g==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.8.0.tgz", + "integrity": "sha512-0SlOTd9R55WEr1GgIXu+hhTT0hzARYx3zIScA5IzpdekZQesI/hKEa5LPHBd415fLkWMjdD59TaW/3qQKpJ0Lg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/mdx-loader": "3.8.0", + "@docusaurus/theme-common": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", "lodash": "^4.17.21", - "reading-time": "^1.5.0", + "schema-dts": "^1.1.2", "srcset": "^4.0.0", "tslib": "^2.6.0", "unist-util-visit": "^5.0.0", @@ -3309,25 +3356,26 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.7.0.tgz", - "integrity": "sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.0.tgz", + "integrity": "sha512-fRDMFLbUN6eVRXcjP8s3Y7HpAt9pzPYh1F/7KKXOCxvJhjjCtbon4VJW0WndEPInVz4t8QUXn5QZkU2tGVCE2g==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/mdx-loader": "3.8.0", + "@docusaurus/module-type-aliases": "3.8.0", + "@docusaurus/theme-common": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "schema-dts": "^1.1.2", "tslib": "^2.6.0", "utility-types": "^3.10.0", "webpack": "^5.88.1" @@ -3341,16 +3389,16 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.7.0.tgz", - "integrity": "sha512-YJSU3tjIJf032/Aeao8SZjFOrXJbz/FACMveSMjLyMH4itQyZ2XgUIzt4y+1ISvvk5zrW4DABVT2awTCqBkx0Q==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.8.0.tgz", + "integrity": "sha512-39EDx2y1GA0Pxfion5tQZLNJxL4gq6susd1xzetVBjVIQtwpCdyloOfQBAgX0FylqQxfJrYqL0DIUuq7rd7uBw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/mdx-loader": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -3363,48 +3411,51 @@ "react-dom": "^18.0.0 || ^19.0.0" } }, - "node_modules/@docusaurus/plugin-debug": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.7.0.tgz", - "integrity": "sha512-Qgg+IjG/z4svtbCNyTocjIwvNTNEwgRjSXXSJkKVG0oWoH0eX/HAPiu+TS1HBwRPQV+tTYPWLrUypYFepfujZA==", + "node_modules/@docusaurus/plugin-css-cascade-layers": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.8.0.tgz", + "integrity": "sha512-/VBTNymPIxQB8oA3ZQ4GFFRYdH4ZxDRRBECxyjRyv486mfUPXfcdk+im4S5mKWa6EK2JzBz95IH/Wu0qQgJ5yQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "fs-extra": "^11.1.1", - "react-json-view-lite": "^1.2.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "tslib": "^2.6.0" }, "engines": { "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" } }, - "node_modules/@docusaurus/plugin-debug/node_modules/react-json-view-lite": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz", - "integrity": "sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==", + "node_modules/@docusaurus/plugin-debug": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.8.0.tgz", + "integrity": "sha512-teonJvJsDB9o2OnG6ifbhblg/PXzZvpUKHFgD8dOL1UJ58u0lk8o0ZOkvaYEBa9nDgqzoWrRk9w+e3qaG2mOhQ==", "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", + "fs-extra": "^11.1.1", + "react-json-view-lite": "^2.3.0", + "tslib": "^2.6.0" + }, "engines": { - "node": ">=14" + "node": ">=18.0" }, "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.7.0.tgz", - "integrity": "sha512-otIqiRV/jka6Snjf+AqB360XCeSv7lQC+DKYW+EUZf6XbuE8utz5PeUQ8VuOcD8Bk5zvT1MC4JKcd5zPfDuMWA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.8.0.tgz", + "integrity": "sha512-aKKa7Q8+3xRSRESipNvlFgNp3FNPELKhuo48Cg/svQbGNwidSHbZT03JqbW4cBaQnyyVchO1ttk+kJ5VC9Gx0w==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "tslib": "^2.6.0" }, "engines": { @@ -3416,14 +3467,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.7.0.tgz", - "integrity": "sha512-M3vrMct1tY65ModbyeDaMoA+fNJTSPe5qmchhAbtqhDD/iALri0g9LrEpIOwNaoLmm6lO88sfBUADQrSRSGSWA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.8.0.tgz", + "integrity": "sha512-ugQYMGF4BjbAW/JIBtVcp+9eZEgT9HRdvdcDudl5rywNPBA0lct+lXMG3r17s02rrhInMpjMahN3Yc9Cb3H5/g==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -3436,14 +3487,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.7.0.tgz", - "integrity": "sha512-X8U78nb8eiMiPNg3jb9zDIVuuo/rE1LjGDGu+5m5CX4UBZzjMy+klOY2fNya6x8ACyE/L3K2erO1ErheP55W/w==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.8.0.tgz", + "integrity": "sha512-9juRWxbwZD3SV02Jd9QB6yeN7eu+7T4zB0bvJLcVQwi+am51wAxn2CwbdL0YCCX+9OfiXbADE8D8Q65Hbopu/w==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "tslib": "^2.6.0" }, "engines": { @@ -3455,17 +3506,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.7.0.tgz", - "integrity": "sha512-bTRT9YLZ/8I/wYWKMQke18+PF9MV8Qub34Sku6aw/vlZ/U+kuEuRpQ8bTcNOjaTSfYsWkK4tTwDMHK2p5S86cA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.8.0.tgz", + "integrity": "sha512-fGpOIyJvNiuAb90nSJ2Gfy/hUOaDu6826e5w5UxPmbpCIc7KlBHNAZ5g4L4ZuHhc4hdfq4mzVBsQSnne+8Ze1g==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -3479,15 +3530,15 @@ } }, "node_modules/@docusaurus/plugin-svgr": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.7.0.tgz", - "integrity": "sha512-HByXIZTbc4GV5VAUkZ2DXtXv1Qdlnpk3IpuImwSnEzCDBkUMYcec5282hPjn6skZqB25M1TYCmWS91UbhBGxQg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.8.0.tgz", + "integrity": "sha512-kEDyry+4OMz6BWLG/lEqrNsL/w818bywK70N1gytViw4m9iAmoxCUT7Ri9Dgs7xUdzCHJ3OujolEmD88Wy44OA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", @@ -3502,25 +3553,26 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.7.0.tgz", - "integrity": "sha512-nPHj8AxDLAaQXs+O6+BwILFuhiWbjfQWrdw2tifOClQoNfuXDjfjogee6zfx6NGHWqshR23LrcN115DmkHC91Q==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/plugin-content-blog": "3.7.0", - "@docusaurus/plugin-content-docs": "3.7.0", - "@docusaurus/plugin-content-pages": "3.7.0", - "@docusaurus/plugin-debug": "3.7.0", - "@docusaurus/plugin-google-analytics": "3.7.0", - "@docusaurus/plugin-google-gtag": "3.7.0", - "@docusaurus/plugin-google-tag-manager": "3.7.0", - "@docusaurus/plugin-sitemap": "3.7.0", - "@docusaurus/plugin-svgr": "3.7.0", - "@docusaurus/theme-classic": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/theme-search-algolia": "3.7.0", - "@docusaurus/types": "3.7.0" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.8.0.tgz", + "integrity": "sha512-qOu6tQDOWv+rpTlKu+eJATCJVGnABpRCPuqf7LbEaQ1mNY//N/P8cHQwkpAU+aweQfarcZ0XfwCqRHJfjeSV/g==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.8.0", + "@docusaurus/plugin-content-blog": "3.8.0", + "@docusaurus/plugin-content-docs": "3.8.0", + "@docusaurus/plugin-content-pages": "3.8.0", + "@docusaurus/plugin-css-cascade-layers": "3.8.0", + "@docusaurus/plugin-debug": "3.8.0", + "@docusaurus/plugin-google-analytics": "3.8.0", + "@docusaurus/plugin-google-gtag": "3.8.0", + "@docusaurus/plugin-google-tag-manager": "3.8.0", + "@docusaurus/plugin-sitemap": "3.8.0", + "@docusaurus/plugin-svgr": "3.8.0", + "@docusaurus/theme-classic": "3.8.0", + "@docusaurus/theme-common": "3.8.0", + "@docusaurus/theme-search-algolia": "3.8.0", + "@docusaurus/types": "3.8.0" }, "engines": { "node": ">=18.0" @@ -3531,24 +3583,24 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.7.0.tgz", - "integrity": "sha512-MnLxG39WcvLCl4eUzHr0gNcpHQfWoGqzADCly54aqCofQX6UozOS9Th4RK3ARbM9m7zIRv3qbhggI53dQtx/hQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/plugin-content-blog": "3.7.0", - "@docusaurus/plugin-content-docs": "3.7.0", - "@docusaurus/plugin-content-pages": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/theme-translations": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.8.0.tgz", + "integrity": "sha512-nQWFiD5ZjoT76OaELt2n33P3WVuuCz8Dt5KFRP2fCBo2r9JCLsp2GJjZpnaG24LZ5/arRjv4VqWKgpK0/YLt7g==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/mdx-loader": "3.8.0", + "@docusaurus/module-type-aliases": "3.8.0", + "@docusaurus/plugin-content-blog": "3.8.0", + "@docusaurus/plugin-content-docs": "3.8.0", + "@docusaurus/plugin-content-pages": "3.8.0", + "@docusaurus/theme-common": "3.8.0", + "@docusaurus/theme-translations": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -3572,15 +3624,15 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.7.0.tgz", - "integrity": "sha512-8eJ5X0y+gWDsURZnBfH0WabdNm8XMCXHv8ENy/3Z/oQKwaB/EHt5lP9VsTDTf36lKEp0V6DjzjFyFIB+CetL0A==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.0.tgz", + "integrity": "sha512-YqV2vAWpXGLA+A3PMLrOMtqgTHJLDcT+1Caa6RF7N4/IWgrevy5diY8oIHFkXR/eybjcrFFjUPrHif8gSGs3Tw==", "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "3.7.0", - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", + "@docusaurus/mdx-loader": "3.8.0", + "@docusaurus/module-type-aliases": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3600,19 +3652,19 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.7.0.tgz", - "integrity": "sha512-Al/j5OdzwRU1m3falm+sYy9AaB93S1XF1Lgk9Yc6amp80dNxJVplQdQTR4cYdzkGtuQqbzUA8+kaoYYO0RbK6g==", - "license": "MIT", - "dependencies": { - "@docsearch/react": "^3.8.1", - "@docusaurus/core": "3.7.0", - "@docusaurus/logger": "3.7.0", - "@docusaurus/plugin-content-docs": "3.7.0", - "@docusaurus/theme-common": "3.7.0", - "@docusaurus/theme-translations": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-validation": "3.7.0", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.0.tgz", + "integrity": "sha512-GBZ5UOcPgiu6nUw153+0+PNWvFKweSnvKIL6Rp04H9olKb475jfKjAwCCtju5D2xs5qXHvCMvzWOg5o9f6DtuQ==", + "license": "MIT", + "dependencies": { + "@docsearch/react": "^3.9.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/plugin-content-docs": "3.8.0", + "@docusaurus/theme-common": "3.8.0", + "@docusaurus/theme-translations": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-validation": "3.8.0", "algoliasearch": "^5.17.1", "algoliasearch-helper": "^3.22.6", "clsx": "^2.0.0", @@ -3631,9 +3683,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.7.0.tgz", - "integrity": "sha512-Ewq3bEraWDmienM6eaNK7fx+/lHMtGDHQyd1O+4+3EsDxxUmrzPkV7Ct3nBWTuE0MsoZr3yNwQVKjllzCMuU3g==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.0.tgz", + "integrity": "sha512-1DTy/snHicgkCkryWq54fZvsAglTdjTx4qjOXgqnXJ+DIty1B+aPQrAVUu8LiM+6BiILfmNxYsxhKTj+BS3PZg==", "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", @@ -3644,9 +3696,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.7.0.tgz", - "integrity": "sha512-kOmZg5RRqJfH31m+6ZpnwVbkqMJrPOG5t0IOl4i/+3ruXyNfWzZ0lVtVrD0u4ONc/0NOsS9sWYaxxWNkH1LdLQ==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.0.tgz", + "integrity": "sha512-RDEClpwNxZq02c+JlaKLWoS13qwWhjcNsi2wG1UpzmEnuti/z1Wx4SGpqbUqRPNSd8QWWePR8Cb7DvG0VN/TtA==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", @@ -3679,15 +3731,16 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.7.0.tgz", - "integrity": "sha512-e7zcB6TPnVzyUaHMJyLSArKa2AG3h9+4CfvKXKKWNx6hRs+p0a+u7HHTJBgo6KW2m+vqDnuIHK4X+bhmoghAFA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.0.tgz", + "integrity": "sha512-2wvtG28ALCN/A1WCSLxPASFBFzXCnP0YKCAFIPcvEb6imNu1wg7ni/Svcp71b3Z2FaOFFIv4Hq+j4gD7gA0yfQ==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.7.0", - "@docusaurus/types": "3.7.0", - "@docusaurus/utils-common": "3.7.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/types": "3.8.0", + "@docusaurus/utils-common": "3.8.0", "escape-string-regexp": "^4.0.0", + "execa": "5.1.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "github-slugger": "^1.5.0", @@ -3697,9 +3750,9 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", + "p-queue": "^6.6.2", "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", - "shelljs": "^0.8.5", "tslib": "^2.6.0", "url-loader": "^4.1.1", "utility-types": "^3.10.0", @@ -3710,12 +3763,12 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.7.0.tgz", - "integrity": "sha512-IZeyIfCfXy0Mevj6bWNg7DG7B8G+S6o6JVpddikZtWyxJguiQ7JYr0SIZ0qWd8pGNuMyVwriWmbWqMnK7Y5PwA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.0.tgz", + "integrity": "sha512-3TGF+wVTGgQ3pAc9+5jVchES4uXUAhAt9pwv7uws4mVOxL4alvU3ue/EZ+R4XuGk94pDy7CNXjRXpPjlfZXQfw==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.7.0", + "@docusaurus/types": "3.8.0", "tslib": "^2.6.0" }, "engines": { @@ -3723,14 +3776,14 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.7.0.tgz", - "integrity": "sha512-w8eiKk8mRdN+bNfeZqC4nyFoxNyI1/VExMKAzD9tqpJfLLbsa46Wfn5wcKH761g9WkKh36RtFV49iL9lh1DYBA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.0.tgz", + "integrity": "sha512-MrnEbkigr54HkdFeg8e4FKc4EF+E9dlVwsY3XQZsNkbv3MKZnbHQ5LsNJDIKDROFe8PBf5C4qCAg5TPBpsjrjg==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.7.0", - "@docusaurus/utils": "3.7.0", - "@docusaurus/utils-common": "3.7.0", + "@docusaurus/logger": "3.8.0", + "@docusaurus/utils": "3.8.0", + "@docusaurus/utils-common": "3.8.0", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -4531,12 +4584,6 @@ "@types/node": "*" } }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "license": "MIT" - }, "node_modules/@types/prismjs": { "version": "1.26.4", "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz", @@ -4969,33 +5016,33 @@ } }, "node_modules/algoliasearch": { - "version": "5.20.4", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.20.4.tgz", - "integrity": "sha512-wjfzqruxovJyDqga8M6Xk5XtfuVg3igrWjhjgkRya87+WwfEa1kg+IluujBLzgAiMSd6rO6jqRb7czjgeeSYgQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-abtesting": "5.20.4", - "@algolia/client-analytics": "5.20.4", - "@algolia/client-common": "5.20.4", - "@algolia/client-insights": "5.20.4", - "@algolia/client-personalization": "5.20.4", - "@algolia/client-query-suggestions": "5.20.4", - "@algolia/client-search": "5.20.4", - "@algolia/ingestion": "1.20.4", - "@algolia/monitoring": "1.20.4", - "@algolia/recommend": "5.20.4", - "@algolia/requester-browser-xhr": "5.20.4", - "@algolia/requester-fetch": "5.20.4", - "@algolia/requester-node-http": "5.20.4" + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.25.0.tgz", + "integrity": "sha512-n73BVorL4HIwKlfJKb4SEzAYkR3Buwfwbh+MYxg2mloFph2fFGV58E90QTzdbfzWrLn4HE5Czx/WTjI8fcHaMg==", + "license": "MIT", + "dependencies": { + "@algolia/client-abtesting": "5.25.0", + "@algolia/client-analytics": "5.25.0", + "@algolia/client-common": "5.25.0", + "@algolia/client-insights": "5.25.0", + "@algolia/client-personalization": "5.25.0", + "@algolia/client-query-suggestions": "5.25.0", + "@algolia/client-search": "5.25.0", + "@algolia/ingestion": "1.25.0", + "@algolia/monitoring": "1.25.0", + "@algolia/recommend": "5.25.0", + "@algolia/requester-browser-xhr": "5.25.0", + "@algolia/requester-fetch": "5.25.0", + "@algolia/requester-node-http": "5.25.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.24.2.tgz", - "integrity": "sha512-vBw/INZDfyh/THbVeDy8On8lZqd2qiUAHde5N4N1ygL4SoeLqLGJ4GHneHrDAYsjikRwTTtodEP0fiXl5MxHFQ==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.25.0.tgz", + "integrity": "sha512-vQoK43U6HXA9/euCqLjvyNdM4G2Fiu/VFp4ae0Gau9sZeIKBPvUPnXfLYAe65Bg7PFuw03coeu5K6lTPSXRObw==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -5141,19 +5188,10 @@ "astring": "bin/astring" } }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "funding": [ { "type": "opencollective", @@ -5170,11 +5208,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -5403,9 +5441,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "funding": [ { "type": "opencollective", @@ -5422,10 +5460,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -5564,9 +5602,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001702", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz", - "integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==", + "version": "1.0.30001720", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", + "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", "funding": [ { "type": "opencollective", @@ -6010,9 +6048,9 @@ } }, "node_modules/consola": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.0.tgz", - "integrity": "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -6147,11 +6185,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", + "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", + "license": "MIT", "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.4" }, "funding": { "type": "opencollective", @@ -6159,9 +6198,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.41.0.tgz", - "integrity": "sha512-71Gzp96T9YPk63aUvE5Q5qP+DryB4ZloUZPSOebGM88VNw8VNfvdA7z6kGA8iGOTEzAomsRidp4jXSmUIJsL+Q==", + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz", + "integrity": "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6493,9 +6532,9 @@ } }, "node_modules/cssdb": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.2.3.tgz", - "integrity": "sha512-9BDG5XmJrJQQnJ51VFxXCAtpZ5ebDlAREmO8sxMOVU0aSxN/gocbctjIG5LMh3WBUq+xTlb/jw2LoljBEqraTA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.3.0.tgz", + "integrity": "sha512-c7bmItIg38DgGjSwDPZOYF/2o0QU/sSgkWOMyl8votOfgFuyiFKWPesmCGEsrGLxEA9uL540cp8LdaGEjUGsZQ==", "funding": [ { "type": "opencollective", @@ -6726,6 +6765,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6794,28 +6834,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "license": "MIT", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6866,38 +6884,6 @@ "node": ">= 4.0.0" } }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "license": "MIT", - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -7067,9 +7053,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.112", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz", - "integrity": "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==", + "version": "1.5.161", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.161.tgz", + "integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7702,15 +7688,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -7815,134 +7792,6 @@ } } }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/form-data-encoder": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", @@ -8152,55 +8001,17 @@ "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/global-dirs/node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" + "engines": { + "node": ">=10" } }, "node_modules/globals": { @@ -8958,13 +8769,10 @@ } }, "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", + "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, "bin": { "image-size": "bin/image-size.js" }, @@ -8972,16 +8780,6 @@ "node": ">=16.x" } }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -9059,14 +8857,6 @@ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -9264,15 +9054,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -9321,15 +9102,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -12355,6 +12127,15 @@ "node": ">=12.20" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -12400,6 +12181,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -12413,13 +12210,16 @@ "node": ">=8" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/package-json": { @@ -12645,79 +12445,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/postcss": { "version": "8.4.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", @@ -12815,9 +12542,9 @@ } }, "node_modules/postcss-color-functional-notation": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.8.tgz", - "integrity": "sha512-S/TpMKVKofNvsxfau/+bw+IA6cSfB6/kmzFj5szUofHOVnFFMB2WwK+Zu07BeMD8T0n+ZnTO5uXiMvAKe2dPkA==", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.10.tgz", + "integrity": "sha512-k9qX+aXHBiLTRrWoCJuUFI6F1iF6QJQUXNVWJVSbqZgj57jDhBlOvD8gNUGl35tgqDivbGLhZeW3Ongz4feuKA==", "funding": [ { "type": "github", @@ -12830,10 +12557,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -12930,9 +12657,9 @@ } }, "node_modules/postcss-custom-media": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.5.tgz", - "integrity": "sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ==", + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz", + "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==", "funding": [ { "type": "github", @@ -12945,10 +12672,10 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -12958,9 +12685,9 @@ } }, "node_modules/postcss-custom-properties": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.4.tgz", - "integrity": "sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A==", + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.5.tgz", + "integrity": "sha512-UWf/vhMapZatv+zOuqlfLmYXeOhhHLh8U8HAKGI2VJ00xLRYoAJh4xv8iX6FB6+TLXeDnm0DBLMi00E0hodbQw==", "funding": [ { "type": "github", @@ -12973,9 +12700,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -12987,9 +12714,9 @@ } }, "node_modules/postcss-custom-selectors": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.4.tgz", - "integrity": "sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz", + "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==", "funding": [ { "type": "github", @@ -13002,9 +12729,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", "postcss-selector-parser": "^7.0.0" }, "engines": { @@ -13129,9 +12856,9 @@ } }, "node_modules/postcss-double-position-gradients": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.0.tgz", - "integrity": "sha512-JkIGah3RVbdSEIrcobqj4Gzq0h53GG4uqDPsho88SgY84WnpkTpI0k50MFK/sX7XqVisZ6OqUfFnoUO6m1WWdg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.2.tgz", + "integrity": "sha512-7qTqnL7nfLRyJK/AHSVrrXOuvDDzettC+wGoienURV8v2svNbu6zJC52ruZtHaO6mfcagFmuTGFdzRsJKB3k5Q==", "funding": [ { "type": "github", @@ -13144,7 +12871,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -13289,9 +13016,9 @@ } }, "node_modules/postcss-lab-function": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.8.tgz", - "integrity": "sha512-plV21I86Hg9q8omNz13G9fhPtLopIWH06bt/Cb5cs1XnaGU2kUtEitvVd4vtQb/VqCdNUHK5swKn3QFmMRbpDg==", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.10.tgz", + "integrity": "sha512-tqs6TCEv9tC1Riq6fOzHuHcZyhg4k3gIAMB8GGY/zA1ssGdm6puHMVE7t75aOSoFg7UD2wyrFFhbldiCMyyFTQ==", "funding": [ { "type": "github", @@ -13304,10 +13031,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -13878,9 +13605,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.1.5.tgz", - "integrity": "sha512-LQybafF/K7H+6fAs4SIkgzkSCixJy0/h0gubDIAP3Ihz+IQBRwsjyvBnAZ3JUHD+A/ITaxVRPDxn//a3Qy4pDw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.0.tgz", + "integrity": "sha512-cl13sPBbSqo1Q7Ryb19oT5NZO5IHFolRbIMdgDq4f9w1MHYiL6uZS7uSsjXJ1KzRIcX5BMjEeyxmAevVXENa3Q==", "funding": [ { "type": "github", @@ -13894,59 +13621,60 @@ "license": "MIT-0", "dependencies": { "@csstools/postcss-cascade-layers": "^5.0.1", - "@csstools/postcss-color-function": "^4.0.8", - "@csstools/postcss-color-mix-function": "^3.0.8", - "@csstools/postcss-content-alt-text": "^2.0.4", - "@csstools/postcss-exponential-functions": "^2.0.7", + "@csstools/postcss-color-function": "^4.0.10", + "@csstools/postcss-color-mix-function": "^3.0.10", + "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.0", + "@csstools/postcss-content-alt-text": "^2.0.6", + "@csstools/postcss-exponential-functions": "^2.0.9", "@csstools/postcss-font-format-keywords": "^4.0.0", - "@csstools/postcss-gamut-mapping": "^2.0.8", - "@csstools/postcss-gradients-interpolation-method": "^5.0.8", - "@csstools/postcss-hwb-function": "^4.0.8", - "@csstools/postcss-ic-unit": "^4.0.0", + "@csstools/postcss-gamut-mapping": "^2.0.10", + "@csstools/postcss-gradients-interpolation-method": "^5.0.10", + "@csstools/postcss-hwb-function": "^4.0.10", + "@csstools/postcss-ic-unit": "^4.0.2", "@csstools/postcss-initial": "^2.0.1", "@csstools/postcss-is-pseudo-class": "^5.0.1", - "@csstools/postcss-light-dark-function": "^2.0.7", + "@csstools/postcss-light-dark-function": "^2.0.9", "@csstools/postcss-logical-float-and-clear": "^3.0.0", "@csstools/postcss-logical-overflow": "^2.0.0", "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", "@csstools/postcss-logical-resize": "^3.0.0", - "@csstools/postcss-logical-viewport-units": "^3.0.3", - "@csstools/postcss-media-minmax": "^2.0.7", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.4", + "@csstools/postcss-logical-viewport-units": "^3.0.4", + "@csstools/postcss-media-minmax": "^2.0.9", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", "@csstools/postcss-nested-calc": "^4.0.0", "@csstools/postcss-normalize-display-values": "^4.0.0", - "@csstools/postcss-oklab-function": "^4.0.8", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", - "@csstools/postcss-random-function": "^1.0.3", - "@csstools/postcss-relative-color-syntax": "^3.0.8", + "@csstools/postcss-oklab-function": "^4.0.10", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", + "@csstools/postcss-random-function": "^2.0.1", + "@csstools/postcss-relative-color-syntax": "^3.0.10", "@csstools/postcss-scope-pseudo-class": "^4.0.1", - "@csstools/postcss-sign-functions": "^1.1.2", - "@csstools/postcss-stepped-value-functions": "^4.0.7", + "@csstools/postcss-sign-functions": "^1.1.4", + "@csstools/postcss-stepped-value-functions": "^4.0.9", "@csstools/postcss-text-decoration-shorthand": "^4.0.2", - "@csstools/postcss-trigonometric-functions": "^4.0.7", + "@csstools/postcss-trigonometric-functions": "^4.0.9", "@csstools/postcss-unset-value": "^4.0.0", - "autoprefixer": "^10.4.19", - "browserslist": "^4.24.4", + "autoprefixer": "^10.4.21", + "browserslist": "^4.24.5", "css-blank-pseudo": "^7.0.1", "css-has-pseudo": "^7.0.2", "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.2.3", + "cssdb": "^8.3.0", "postcss-attribute-case-insensitive": "^7.0.1", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^7.0.8", + "postcss-color-functional-notation": "^7.0.10", "postcss-color-hex-alpha": "^10.0.0", "postcss-color-rebeccapurple": "^10.0.0", - "postcss-custom-media": "^11.0.5", - "postcss-custom-properties": "^14.0.4", - "postcss-custom-selectors": "^8.0.4", + "postcss-custom-media": "^11.0.6", + "postcss-custom-properties": "^14.0.5", + "postcss-custom-selectors": "^8.0.5", "postcss-dir-pseudo-class": "^9.0.1", - "postcss-double-position-gradients": "^6.0.0", + "postcss-double-position-gradients": "^6.0.2", "postcss-focus-visible": "^10.0.1", "postcss-focus-within": "^9.0.1", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^6.0.0", "postcss-image-set-function": "^7.0.0", - "postcss-lab-function": "^7.0.8", + "postcss-lab-function": "^7.0.10", "postcss-logical": "^8.1.0", "postcss-nesting": "^13.0.1", "postcss-opacity-percentage": "^3.0.0", @@ -14307,15 +14035,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -14421,132 +14140,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", - "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/react-dev-utils/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/react-dom": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", @@ -14559,12 +14152,6 @@ "react": "^19.1.0" } }, - "node_modules/react-error-overlay": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.1.0.tgz", - "integrity": "sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==", - "license": "MIT" - }, "node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", @@ -14594,6 +14181,18 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-json-view-lite": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.4.1.tgz", + "integrity": "sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, "node_modules/react-loadable": { "name": "@docusaurus/react-loadable", "version": "6.0.0", @@ -14697,35 +14296,6 @@ "node": ">=8.10.0" } }, - "node_modules/reading-time": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", - "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==", - "license": "MIT" - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "license": "MIT", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -15256,6 +14826,12 @@ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT" }, + "node_modules/schema-dts": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz", + "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==", + "license": "Apache-2.0" + }, "node_modules/schema-utils": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", @@ -15598,22 +15174,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -15885,9 +15445,9 @@ } }, "node_modules/std-env": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.1.tgz", - "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", "license": "MIT" }, "node_modules/string_decoder": { @@ -16230,12 +15790,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "license": "MIT" - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -16252,6 +15806,15 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, + "node_modules/tinypool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", + "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -16363,6 +15926,7 @@ "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "optional": true, "peer": true, "bin": { "tsc": "bin/tsc", @@ -16561,9 +16125,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -16578,9 +16142,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -17445,19 +17010,10 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, "node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", "license": "MIT", "engines": { "node": ">=12.20" diff --git a/docs/package.json b/docs/package.json index 7c155bd9e..2bb440711 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,9 +14,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/plugin-client-redirects": "^3.7.0", - "@docusaurus/preset-classic": "3.7.0", + "@docusaurus/core": "3.8.0", + "@docusaurus/plugin-client-redirects": "^3.8.0", + "@docusaurus/preset-classic": "3.8.0", "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -25,8 +25,8 @@ "react-dom": "^19.1.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/types": "3.7.0" + "@docusaurus/module-type-aliases": "3.8.0", + "@docusaurus/types": "3.8.0" }, "browserslist": { "production": [ From cbd8f3a870580e55892e2e207da8d45bcc2007ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 11:57:19 -0400 Subject: [PATCH 111/282] chore(deps): bump mypy from 1.15.0 to 1.16.0 (#1309) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin --- requirements/tools.txt | 2 +- slack_bolt/adapter/falcon/async_resource.py | 2 +- slack_bolt/adapter/falcon/resource.py | 4 ++-- slack_bolt/app/app.py | 2 +- slack_bolt/app/async_app.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 0fd423ad2..0e6090497 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.15.0 +mypy==1.16.0 flake8==7.2.0 black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version diff --git a/slack_bolt/adapter/falcon/async_resource.py b/slack_bolt/adapter/falcon/async_resource.py index eece0a323..8d03b456c 100644 --- a/slack_bolt/adapter/falcon/async_resource.py +++ b/slack_bolt/adapter/falcon/async_resource.py @@ -42,7 +42,7 @@ async def on_get(self, req: Request, resp: Response): resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment] + resp.body = "The page is not found..." async def on_post(self, req: Request, resp: Response): bolt_req = await self._to_bolt_request(req) diff --git a/slack_bolt/adapter/falcon/resource.py b/slack_bolt/adapter/falcon/resource.py index baf0f9745..53792775f 100644 --- a/slack_bolt/adapter/falcon/resource.py +++ b/slack_bolt/adapter/falcon/resource.py @@ -36,7 +36,7 @@ def on_get(self, req: Request, resp: Response): resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment] + resp.body = "The page is not found..." def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -53,7 +53,7 @@ def _to_bolt_request(self, req: Request) -> BoltRequest: def _write_response(self, bolt_resp: BoltResponse, resp: Response): if falcon_version.__version__.startswith("2."): # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = bolt_resp.body # type: ignore[assignment] + resp.body = bolt_resp.body else: resp.text = bolt_resp.body diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index 69da0a0d8..c117740a1 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -1448,7 +1448,7 @@ def _register_listener( CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, + lazy_functions=functions, # type:ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 3fcc3d955..c04326291 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -1487,7 +1487,7 @@ def _register_listener( AsyncCustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, + lazy_functions=functions, # type:ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, From 260e9b9c34ac930c99769077ee682dda9e0f1426 Mon Sep 17 00:00:00 2001 From: Tracy Rericha <108959677+technically-tracy@users.noreply.github.com> Date: Tue, 3 Jun 2025 12:09:24 -0400 Subject: [PATCH 112/282] docs: updated nav (#1313) --- docs/sidebars.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/sidebars.js b/docs/sidebars.js index 82209d428..65c5e85f4 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -40,8 +40,14 @@ const sidebars = { ], }, "concepts/ai-apps", - "concepts/custom-steps", - "concepts/custom-steps-dynamic-options", + { + type: 'category', + label: 'Custom Steps', + items: [ + 'concepts/custom-steps', + 'concepts/custom-steps-dynamic-options', + ] + }, { type: "category", label: "App Configuration", From 5778bf59c6c28379faa2ffee62c0b1e862b2a3f0 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 26 Jun 2025 13:22:57 -0400 Subject: [PATCH 113/282] fix: sanic dependencies for tests (#1320) --- .github/workflows/tests.yml | 14 +++++++------- requirements/adapter.txt | 5 +++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a1135f265..1d16d1a1b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,20 +53,20 @@ jobs: - name: Install async dependencies run: | pip install -r requirements/async.txt - - name: Run tests for HTTP Mode adapters (ASGI) - run: | - # Requires async test dependencies - pytest tests/adapter_tests/asgi/ --junitxml=reports/test_adapter_asgi.xml - name: Run tests for Socket Mode adapters run: | # Requires async test dependencies pytest tests/adapter_tests/socket_mode/ --junitxml=reports/test_adapter_socket_mode.xml - - name: Run tests for HTTP Mode adapters (asyncio-based libraries) - run: | - pytest tests/adapter_tests_async/ --junitxml=reports/test_adapter_async.xml - name: Install all dependencies run: | pip install -r requirements/testing.txt + - name: Run tests for HTTP Mode adapters (ASGI) + run: | + # Requires async test dependencies + pytest tests/adapter_tests/asgi/ --junitxml=reports/test_adapter_asgi.xml + - name: Run tests for HTTP Mode adapters (asyncio-based libraries) + run: | + pytest tests/adapter_tests_async/ --junitxml=reports/test_adapter_async.xml - name: Run asynchronous tests run: | pytest tests/slack_bolt_async/ --junitxml=reports/test_slack_bolt_async.xml diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 6618f2b6f..b8cadb510 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -13,9 +13,14 @@ fastapi>=0.70.0,<1 Flask>=1,<4 Werkzeug>=2,<4 pyramid>=1,<3 + +# Sanic and its dependencies +# Note: Sanic imports tracerite with wild card versions +tracerite<1.1.2; python_version<="3.8" # older versions of python are not compatible with tracerite>1.1.2 sanic>=20,<21; python_version=="3.6" sanic>=21,<24; python_version>"3.6" and python_version<="3.8" sanic>=21,<26; python_version>"3.8" + starlette>=0.19.1,<1 tornado>=6,<7 uvicorn<1 # The oldest version can vary among Python runtime versions From 7f9ae9cdb68c4f98995f557c2fec6bbbfaf62239 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:06:07 -0700 Subject: [PATCH 114/282] chore(deps): bump brace-expansion from 1.1.11 to 1.1.12 in /docs (#1321) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 303f5487a..4e7d7b445 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5421,9 +5421,10 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" From 35295329f4118c16d2cc3e4545661a98eac7b192 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 27 Jun 2025 12:59:44 -0700 Subject: [PATCH 115/282] ci: run unit tests on main once a day (#1319) Co-authored-by: William Bergamin --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1d16d1a1b..748ddcd30 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,8 @@ on: branches: - main pull_request: + schedule: + - cron: "0 0 * * *" jobs: build: From 079552dc861d710b846a4bdb4044cb6431a5f304 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 27 Jun 2025 16:29:56 -0400 Subject: [PATCH 116/282] chore: run CI tests from the Github UI (#1322) --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 748ddcd30..3b396b201 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,6 +7,7 @@ on: pull_request: schedule: - cron: "0 0 * * *" + workflow_dispatch: jobs: build: From d3ca0550196a2211c991f5db34ff40945e980095 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 27 Jun 2025 14:10:07 -0700 Subject: [PATCH 117/282] ci: send a notification of failing tests on the main branch (#1323) Co-authored-by: William Bergamin --- .github/workflows/tests.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3b396b201..4fe757b1a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -82,3 +82,18 @@ jobs: flags: ${{ matrix.python-version }} token: ${{ secrets.CODECOV_TOKEN }} verbose: true + notifications: + name: Regression notifications + runs-on: ubuntu-latest + needs: build + if: failure() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' + steps: + - name: Send notifications of failing tests + uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0 + with: + errors: true + webhook: ${{ secrets.SLACK_REGRESSION_FAILURES_WEBHOOK_URL }} + webhook-type: webhook-trigger + payload: | + action_url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + repository: "${{ github.repository }}" From b9c9999b1d3777997af1c8f57a4d59aa9e148256 Mon Sep 17 00:00:00 2001 From: Tracy Rericha <108959677+technically-tracy@users.noreply.github.com> Date: Tue, 1 Jul 2025 06:57:34 -0400 Subject: [PATCH 118/282] docs: add quick start guide using the slack cli and terminal (#1317) --- docs/content/building-an-app.md | 483 +++++++++++++++++++++++++++++ docs/content/getting-started.md | 527 +++++++++++--------------------- docs/docusaurus.config.js | 1 + docs/sidebars.js | 6 +- 4 files changed, 662 insertions(+), 355 deletions(-) create mode 100644 docs/content/building-an-app.md diff --git a/docs/content/building-an-app.md b/docs/content/building-an-app.md new file mode 100644 index 000000000..deb3146b9 --- /dev/null +++ b/docs/content/building-an-app.md @@ -0,0 +1,483 @@ +--- +title: Building an App with Bolt for Python +sidebar_label: Building an App +--- + +# Building an App with Bolt for Python + +This guide is meant to walk you through getting up and running with a Slack app using Bolt for Python. Along the way, we’ll create a new Slack app, set up your local environment, and develop an app that listens and responds to messages from a Slack workspace. + +When you're finished, you'll have created the [Getting Started app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started) to run, modify, and make your own. ⚡️ + +--- + +### Create an app {#create-an-app} +First thing's first: before you start developing with Bolt, you'll want to [create a Slack app](https://api.slack.com/apps/new). + +:::tip[A place to test and learn] + +We recommend using a workspace where you won't disrupt real work getting done — [you can create a new one for free](https://slack.com/get-started#create). + +::: + +After you fill out an app name (_you can change it later_) and pick a workspace to install it to, hit the `Create App` button and you'll land on your app's **Basic Information** page. + +This page contains an overview of your app in addition to important credentials you'll need later. + +![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") + +Look around, add an app icon and description, and then let's start configuring your app 🔩 + +--- + +### Tokens and installing apps {#tokens-and-installing-apps} +Slack apps use [OAuth to manage access to Slack's APIs](https://docs.slack.dev/authentication/installing-with-oauth). When an app is installed, you'll receive a token that the app can use to call API methods. + +There are three main token types available to a Slack app: user (`xoxp`), bot (`xoxb`), and app-level (`xapp`) tokens. +- [User tokens](https://docs.slack.dev/authentication/tokens#user) allow you to call API methods on behalf of users after they install or authenticate the app. There may be several user tokens for a single workspace. +- [Bot tokens](https://docs.slack.dev/authentication/tokens#bot) are associated with bot users, and are only granted once in a workspace where someone installs the app. The bot token your app uses will be the same no matter which user performed the installation. Bot tokens are the token type that _most_ apps use. +- [App-level tokens](https://docs.slack.dev/authentication/tokens#app-level) represent your app across organizations, including installations by all individual users on all workspaces in a given organization and are commonly used for creating WebSocket connections to your app. + +We're going to use bot and app-level tokens for this guide. + +1. Navigate to **OAuth & Permissions** on the left sidebar and scroll down to the **Bot Token Scopes** section. Click **Add an OAuth Scope**. + +2. For now, we'll just add one scope: [`chat:write`](https://docs.slack.dev/reference/scopes/chat.write). This grants your app the permission to post messages in channels it's a member of. + +3. Scroll up to the top of the **OAuth & Permissions** page and click **Install App to Workspace**. You'll be led through Slack's OAuth UI, where you should allow your app to be installed to your development workspace. + +4. Once you authorize the installation, you'll land on the **OAuth & Permissions** page and see a **Bot User OAuth Access Token**. + +![OAuth Tokens](/img/boltpy/bot-token.png "Bot OAuth Token") + +5. Head over to **Basic Information** and scroll down under the App Token section and click **Generate Token and Scopes** to generate an app-level token. Add the `connections:write` scope to this token and save the generated `xapp` token. + +6. Navigate to **Socket Mode** on the left side menu and toggle to enable. + +:::tip[Not sharing is sometimes caring] + +Treat your tokens like passwords and [keep them safe](https://docs.slack.dev/authentication/best-practices-for-security). Your app uses tokens to post and retrieve information from Slack workspaces. + +::: + +--- + +### Setting up your project {#setting-up-your-project} + +With the initial configuration handled, it's time to set up a new Bolt project. This is where you'll write the code that handles the logic for your app. + +If you don’t already have a project, let’s create a new one. Create an empty directory: + +```sh +$ mkdir first-bolt-app +$ cd first-bolt-app +``` + +Next, we recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.6 or later](https://www.python.org/downloads/): + +```sh +$ python3 -m venv .venv +$ source .venv/bin/activate +$ pip install -r requirements.txt +``` + +We can confirm that the virtual environment is active by checking that the path to `python3` is _inside_ your project ([a similar command is available on Windows](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#activating-a-virtual-environment)): + +```sh +$ which python3 +# Output: /path/to/first-bolt-app/.venv/bin/python3 +``` + +Before we install the Bolt for Python package to your new project, let's save the **bot token** and **app-level token** that were generated when you configured your app. + +1. **Copy your bot (xoxb) token from the OAuth & Permissions page** and store it in a new environment variable. The following example works on Linux and macOS; but [similar commands are available on Windows](https://superuser.com/questions/212150/how-to-set-env-variable-in-windows-cmd-line/212153#212153). + +```sh +$ export SLACK_BOT_TOKEN=xoxb- +``` + +2. **Copy your app-level (xapp) token from the Basic Information page** and then store it in a new environment variable. + +```sh +$ export SLACK_APP_TOKEN= +``` + +:::warning[Keep it secret. Keep it safe.] + +Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](https://docs.slack.dev/authentication/best-practices-for-security). + +::: + +Now, let's create your app. Install the `slack_bolt` Python package to your virtual environment using the following command: + +```sh +$ pip install slack_bolt +``` + +Create a new file called `app.py` in this directory and add the following code: + +```python +import os +from slack_bolt import App +from slack_bolt.adapter.socket_mode import SocketModeHandler + +# Initializes your app with your bot token and socket mode handler +app = App(token=os.environ.get("SLACK_BOT_TOKEN")) + +# Start your app +if __name__ == "__main__": + SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() +``` + +Your tokens are enough to create your first Bolt app. Save your `app.py` file then on the command line run the following: + +```sh +$ python3 app.py +``` + +Your app should let you know that it's up and running. 🎉 + +--- + +### Setting up events {#setting-up-events} +Your app behaves similarly to people on your team — it can post messages, add emoji reactions, and listen and respond to events. + +To listen for events happening in a Slack workspace (like when a message is posted or when a reaction is posted to a message) you'll use the [Events API to subscribe to event types](https://docs.slack.dev/apis/events-api/). + +For those just starting, we recommend using [Socket Mode](https://docs.slack.dev/apis/events-api/using-socket-mode). Socket Mode allows your app to use the Events API and interactive features without exposing a public HTTP Request URL. This can be helpful during development, or if you're receiving requests from behind a firewall. + +That being said, you're welcome to set up an app with a public HTTP Request URL. HTTP is more useful for apps being deployed to hosting environments to respond within a large corporate Slack workspaces/organization, or apps intended for distribution via the Slack Marketplace. + +We've provided instructions for both ways in this guide. + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +1. Head to your app's configuration page (click on the app [from your app settings page](https://api.slack.com/apps)). Navigate to **Socket Mode** on the left side menu and toggle to enable. + +2. Go to **Basic Information** and scroll down under the App-Level Tokens section and click **Generate Token and Scopes** to generate an app-level token. Add the `connections:write` scope to this token and save the generated `xapp` token, we'll use that in just a moment. + +3. Finally, it's time to tell Slack what events we'd like to listen for. Under **Event Subscriptions**, toggle the switch labeled **Enable Events**. + +When an event occurs, Slack will send your app some information about the event, like the user that triggered it and the channel it occurred in. Your app will process the details and can respond accordingly. + + + + +1. Go back to your app configuration page (click on the app [from your app management page](https://api.slack.com/apps)). Click **Event Subscriptions** on the left sidebar. Toggle the switch labeled **Enable Events**. + +2. Add your Request URL. Slack will send HTTP POST requests corresponding to events to this [Request URL](https://docs.slack.dev/apis/events-api/#subscribing) endpoint. Bolt uses the `/slack/events` path to listen to all incoming requests (whether shortcuts, events, or interactivity payloads). When configuring your Request URL within your app configuration, you'll append `/slack/events`, e.g. `https:///slack/events`. 💡 As long as your Bolt app is still running, your URL should become verified. + +:::tip[Using proxy services] + +For local development, you can use a proxy service like ngrok to create a public URL and tunnel requests to your development environment. Refer to [ngrok's getting started guide](https://ngrok.com/docs#getting-started-expose) on how to create this tunnel. And when you get to hosting your app, we've collected some of the most common hosting providers Slack developers use to host their apps [on our API site](https://docs.slack.dev/distribution/hosting-slack-apps/). + +::: + + + + +Navigate to **Event Subscriptions** on the left sidebar and toggle to enable. Under **Subscribe to Bot Events**, you can add events for your bot to respond to. There are four events related to messages: +- [`message.channels`](https://docs.slack.dev/reference/events/message.channels) listens for messages in public channels that your app is added to. +- [`message.groups`](https://docs.slack.dev/reference/events/message.groups) listens for messages in 🔒 private channels that your app is added to. +- [`message.im`](https://docs.slack.dev/reference/events/message.im) listens for messages in your app's DMs with users. +- [`message.mpim`](https://docs.slack.dev/reference/events/message.mpim) listens for messages in multi-person DMs that your app is added to. + +If you want your bot to listen to messages from everywhere it is added to, choose all four message events. After you’ve selected the events you want your bot to listen to, click the green **Save Changes** button. + +--- + +### Listening and responding to a message {#listening-and-responding-to-a-message} +Your app is now ready for some logic. Let's start by using the `message()` method to attach a listener for messages. + +The following example listens and responds to all messages in channels/DMs where your app has been added that contain the word "hello": + + + + +```python +import os +from slack_bolt import App +from slack_bolt.adapter.socket_mode import SocketModeHandler + +# Initializes your app with your bot token and socket mode handler +app = App(token=os.environ.get("SLACK_BOT_TOKEN")) + +# Listens to incoming messages that contain "hello" +# To learn available listener arguments, +# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +@app.message("hello") +def message_hello(message, say): + # say() sends a message to the channel where the event was triggered + say(f"Hey there <@{message['user']}>!") + +# Start your app +if __name__ == "__main__": + SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() +``` + + + + +```python +import os +from slack_bolt import App + +# Initializes your app with your bot token and signing secret +app = App( + token=os.environ.get("SLACK_BOT_TOKEN"), + signing_secret=os.environ.get("SLACK_SIGNING_SECRET") +) + +# Listens to incoming messages that contain "hello" +# To learn available listener arguments, +# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +@app.message("hello") +def message_hello(message, say): + # say() sends a message to the channel where the event was triggered + say(f"Hey there <@{message['user']}>!") + +# Start your app +if __name__ == "__main__": + app.start(port=int(os.environ.get("PORT", 3000))) +``` + + + + +If you restart your app, so long as your bot user has been added to the channel or DM conversation, when you send any message that contains "hello", it will respond. + +This is a basic example, but it gives you a place to start customizing your app based on your own goals. Let's try something a little more interactive by sending a button rather than plain text. + +--- + +### Sending and responding to actions {#sending-and-responding-to-actions} + +To use features like buttons, select menus, datepickers, modals, and shortcuts, you’ll need to enable interactivity. Head over to **Interactivity & Shortcuts** in your app configuration. + + + + +With Socket Mode on, basic interactivity is enabled by default, so no further action is needed. + + + + +Similar to events, you'll need to specify a URL for Slack to send the action (such as _user clicked a button_). Back on your app configuration page, click on **Interactivity & Shortcuts** on the left side. You'll see that there's another **Request URL** box. + +:::tip + +By default, Bolt is configured to use the same endpoint for interactive components that it uses for events, so use the same request URL as above (for example, `https://8e8ec2d7.ngrok.io/slack/events`). Press the **Save Changes** button in the lower right hand corner, and that's it. Your app is set up to handle interactivity! + +::: + + + + +When interactivity is enabled, interactions with shortcuts, modals, or interactive components (such as buttons, select menus, and datepickers) will be sent to your app as events. + +Now, let's go back to your app's code and add logic to handle those events: +- First, we'll send a message that contains an interactive component (in this case a button). +- Next, we'll listen for the action of a user clicking the button before responding. + +Below, the code from the last section is modified to send a message containing a button rather than just a string: + + + + +```python +import os +from slack_bolt import App +from slack_bolt.adapter.socket_mode import SocketModeHandler + +# Initializes your app with your bot token and socket mode handler +app = App( + token=os.environ.get("SLACK_BOT_TOKEN"), + # signing_secret=os.environ.get("SLACK_SIGNING_SECRET") # not required for socket mode +) + +# Listens to incoming messages that contain "hello" +@app.message("hello") +def message_hello(message, say): + # say() sends a message to the channel where the event was triggered + say( + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, + "accessory": { + "type": "button", + "text": {"type": "plain_text", "text": "Click Me"}, + "action_id": "button_click" + } + } + ], + text=f"Hey there <@{message['user']}>!" + ) + +# Start your app +if __name__ == "__main__": + SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() + +``` + + + + +```python +import os +from slack_bolt import App + +# Initializes your app with your bot token and signing secret +app = App( + token=os.environ.get("SLACK_BOT_TOKEN"), + signing_secret=os.environ.get("SLACK_SIGNING_SECRET") +) + +# Listens to incoming messages that contain "hello" +@app.message("hello") +def message_hello(message, say): + # say() sends a message to the channel where the event was triggered + say( + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, + "accessory": { + "type": "button", + "text": {"type": "plain_text", "text": "Click Me"}, + "action_id": "button_click" + } + } + ], + text=f"Hey there <@{message['user']}>!" + ) + +# Start your app +if __name__ == "__main__": + app.start(port=int(os.environ.get("PORT", 3000))) +``` + + + + +The value inside of `say()` is now an object that contains an array of `blocks`. Blocks are the building components of a Slack message and can range from text to images to datepickers. In this case, your app will respond with a section block that includes a button as an accessory. Since we're using `blocks`, the `text` is a fallback for notifications and accessibility. + +You'll notice in the button `accessory` object, there is an `action_id`. This will act as a unique identifier for the button so your app can specify which action it wants to respond to. + +:::tip[Using Block Kit Builder] + +The [Block Kit Builder](https://app.slack.com/block-kit-builder) is an simple way to prototype your interactive messages. The builder lets you (or anyone on your team) mock up messages and generates the corresponding JSON that you can paste directly in your app. + +::: + +Now, if you restart your app and say "hello" in a channel your app is in, you'll see a message with a button. But if you click the button, nothing happens (_yet!_). + +Let's add a handler to send a follow-up message when someone clicks the button: + + + + +```python +import os +from slack_bolt import App +from slack_bolt.adapter.socket_mode import SocketModeHandler + +# Initializes your app with your bot token and socket mode handler +app = App(token=os.environ.get("SLACK_BOT_TOKEN")) + +# Listens to incoming messages that contain "hello" +@app.message("hello") +def message_hello(message, say): + # say() sends a message to the channel where the event was triggered + say( + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, + "accessory": { + "type": "button", + "text": {"type": "plain_text", "text": "Click Me"}, + "action_id": "button_click" + } + } + ], + text=f"Hey there <@{message['user']}>!" + ) + +@app.action("button_click") +def action_button_click(body, ack, say): + # Acknowledge the action + ack() + say(f"<@{body['user']['id']}> clicked the button") + +# Start your app +if __name__ == "__main__": + SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() +``` + + + + +```python +import os +from slack_bolt import App + +# Initializes your app with your bot token and signing secret +app = App( + token=os.environ.get("SLACK_BOT_TOKEN"), + signing_secret=os.environ.get("SLACK_SIGNING_SECRET") +) + +# Listens to incoming messages that contain "hello" +@app.message("hello") +def message_hello(message, say): + # say() sends a message to the channel where the event was triggered + say( + blocks=[ + { + "type": "section", + "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, + "accessory": { + "type": "button", + "text": {"type": "plain_text", "text": "Click Me"}, + "action_id": "button_click" + } + } + ], + text=f"Hey there <@{message['user']}>!" + ) + +@app.action("button_click") +def action_button_click(body, ack, say): + # Acknowledge the action + ack() + say(f"<@{body['user']['id']}> clicked the button") + +# Start your app +if __name__ == "__main__": + app.start(port=int(os.environ.get("PORT", 3000))) +``` + + + + +You can see that we used `app.action()` to listen for the `action_id` that we named `button_click`. If you restart your app and click the button, you'll see a new message from your app that says you clicked the button. + +--- + +### Next steps {#next-steps} +You just built your first [Bolt for Python app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started)! 🎉 + +Now that you have a basic app up and running, you can start exploring how to make your Bolt app stand out. Here are some ideas about what to explore next: + +* Read through the concepts pages to learn about the different methods and features your Bolt app has access to. + +* Explore the different events your bot can listen to with the [`app.event()`](/concepts/event-listening) method. All of the events are listed [on the API docs site](https://docs.slack.dev/reference/events). + +* Bolt allows you to [call Web API methods](/concepts/web-api) with the client attached to your app. There are [over 200 methods](https://docs.slack.dev/reference/methods) on our API site. + +* Learn more about the different token types [on the API docs site](https://docs.slack.dev/authentication/tokens). Your app may need different tokens depending on the actions you want it to perform. diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index d1b7d5e2c..a794f3176 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -1,481 +1,300 @@ --- -title: Getting Started -slug: getting-started -lang: en +title: Quickstart guide with Bolt for Python +sidebar_label: Quickstart --- # Getting started with Bolt for Python -This guide is meant to walk you through getting up and running with a Slack app using Bolt for Python. Along the way, we’ll create a new Slack app, set up your local environment, and develop an app that listens and responds to messages from a Slack workspace. +This quickstart guide aims to help you get a Slack app using Bolt for Python up and running as soon as possible! -When you're finished, you'll have this ⚡️[Getting Started with Slack app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started) to run, modify, and make your own. - ---- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; -### Create an app {#create-an-app} -First thing's first: before you start developing with Bolt, you'll want to [create a Slack app](https://api.slack.com/apps/new). +When complete, you'll have a local environment configured with a customized [app](https://github.com/slack-samples/bolt-python-getting-started-app) running to modify and make your own. -:::tip +:::tip[Reference for readers] -We recommend using a workspace where you won't disrupt real work getting done — [you can create a new one for free](https://slack.com/get-started#create). +In search of the complete guide to building an app from scratch? Check out the [building an app](/building-an-app) guide. ::: -After you fill out an app name (_you can change it later_) and pick a workspace to install it to, hit the `Create App` button and you'll land on your app's **Basic Information** page. +#### Prerequisites -This page contains an overview of your app in addition to important credentials you'll need later. +A few tools are needed for the following steps. We recommend using the [**Slack CLI**](https://tools.slack.dev/slack-cli/) for the smoothest experience, but other options remain available. -![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") +You can also begin by installing git and downloading [Python 3.6 or later](https://www.python.org/downloads/), or the latest stable version of Python. Refer to [Python's setup and building guide](https://devguide.python.org/getting-started/setup-building/) for more details. -Look around, add an app icon and description, and then let's start configuring your app 🔩 +Install the latest version of the Slack CLI to get started: ---- +- [Slack CLI for macOS & Linux](https://tools.slack.dev/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux) +- [Slack CLI for Windows](https://tools.slack.dev/slack-cli/guides/installing-the-slack-cli-for-windows) -### Tokens and installing apps {#tokens-and-installing-apps} -Slack apps use [OAuth to manage access to Slack's APIs](https://docs.slack.dev/authentication/installing-with-oauth). When an app is installed, you'll receive a token that the app can use to call API methods. +Then confirm a successful installation with the following command: -There are three main token types available to a Slack app: user (`xoxp`), bot (`xoxb`), and app-level (`xapp`) tokens. -- [User tokens](https://docs.slack.dev/authentication/tokens#user) allow you to call API methods on behalf of users after they install or authenticate the app. There may be several user tokens for a single workspace. -- [Bot tokens](https://docs.slack.dev/authentication/tokens#bot) are associated with bot users, and are only granted once in a workspace where someone installs the app. The bot token your app uses will be the same no matter which user performed the installation. Bot tokens are the token type that _most_ apps use. -- [App-level tokens](https://docs.slack.dev/authentication/tokens#app-level) represent your app across organizations, including installations by all individual users on all workspaces in a given organization and are commonly used for creating WebSocket connections to your app. +```sh +$ slack version +``` -We're going to use bot and app-level tokens for this guide. +An authenticated login is also required if this hasn't been done before: -1. Navigate to the **OAuth & Permissions** on the left sidebar and scroll down to the **Bot Token Scopes** section. Click **Add an OAuth Scope**. +```sh +$ slack login +``` -2. For now, we'll just add one scope: [`chat:write`](https://docs.slack.dev/reference/scopes/chat.write). This grants your app the permission to post messages in channels it's a member of. +:::info[A place to belong] -3. Scroll up to the top of the **OAuth & Permissions** page and click **Install App to Workspace**. You'll be led through Slack's OAuth UI, where you should allow your app to be installed to your development workspace. +A workspace where development can happen is also needed. -4. Once you authorize the installation, you'll land on the **OAuth & Permissions** page and see a **Bot User OAuth Access Token**. +We recommend using [developer sandboxes](https://docs.slack.dev/tools/developer-sandboxes) to avoid disruptions where real work gets done. -![OAuth Tokens](/img/boltpy/bot-token.png "Bot OAuth Token") +::: -5. Then head over to **Basic Information** and scroll down under the App Token section and click **Generate Token and Scopes** to generate an app-level token. Add the `connections:write` scope to this token and save the generated `xapp` token, we'll use both these tokens in just a moment. +## Creating a project {#creating-a-project} -6. Navigate to **Socket Mode** on the left side menu and toggle to enable. +With the toolchain configured, it's time to set up a new Bolt project. This contains the code that handles logic for your app. -:::tip +If you don’t already have a project, let’s create a new one! -Treat your tokens like passwords and [keep them safe](https://docs.slack.dev/authentication/best-practices-for-security). Your app uses tokens to post and retrieve information from Slack workspaces. + + -::: +A starter template can be used to start with project scaffolding: ---- - -### Setting up your project {#setting-up-your-project} +```sh +$ slack create first-bolt-app --template slack-samples/bolt-python-getting-started-app +$ cd first-bolt-app +``` -With the initial configuration handled, it's time to set up a new Bolt project. This is where you'll write the code that handles the logic for your app. +After a project is created you'll have a `requirements.txt` file for app dependencies and a `.slack` directory for Slack CLI configuration. -If you don’t already have a project, let’s create a new one. Create an empty directory: +A few other files exist too, but we'll visit these later. -```shell -mkdir first-bolt-app -cd first-bolt-app -``` + + -Next, we recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.6 or later](https://www.python.org/downloads/): +A starter template can be cloned to start with project scaffolding: -```shell -python3 -m venv .venv -source .venv/bin/activate +```sh +$ git clone https://github.com/slack-samples/bolt-python-getting-started-app first-bolt-app +$ cd first-bolt-app ``` -We can confirm that the virtual environment is active by checking that the path to `python3` is _inside_ your project ([a similar command is available on Windows](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#activating-a-virtual-environment)): +Outlines of a project are taking shape, so we can move on to running the app! -```shell -which python3 -# Output: /path/to/first-bolt-app/.venv/bin/python3 -``` + + -Before we install the Bolt for Python package to your new project, let's save the **bot token** and **app-level token** that were generated when you configured your app. +We recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.6 or later](https://www.python.org/downloads/): -1. **Copy your bot (xoxb) token from the OAuth & Permissions page** and store it in a new environment variable. The following example works on Linux and macOS; but [similar commands are available on Windows](https://superuser.com/questions/212150/how-to-set-env-variable-in-windows-cmd-line/212153#212153). -```shell -export SLACK_BOT_TOKEN=xoxb- +```sh +$ python3 -m venv .venv +$ source .venv/bin/activate +$ pip install -r requirements.txt ``` -2. **Copy your app-level (xapp) token from the Basic Information page** and then store it in a new environment variable. -```shell -export SLACK_APP_TOKEN= +Confirm the virtual environment is active by checking that the path to `python3` is _inside_ your project ([a similar command is available on Windows](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#activating-a-virtual-environment)): + +```sh +$ which python3 +# Output: /path/to/first-bolt-app/.venv/bin/python3 ``` -:::warning +## Running the app {#running-the-app} -Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](https://docs.slack.dev/authentication/best-practices-for-security). +Before you can start developing with Bolt, you will want a running Slack app. -::: + + -Now, let's create your app. Install the `slack_bolt` Python package to your virtual environment using the following command: +The getting started app template contains a `manifest.json` file with details about an app that we will use to get started. Use the following command and select "Create a new app" to install the app to the team of choice: -```shell -pip install slack_bolt +```sh +$ slack run +... +⚡️ Bolt app is running! ``` -Create a new file called `app.py` in this directory and add the following code: +With the app running, you can test it out with the following steps in Slack: -```python -import os -from slack_bolt import App -from slack_bolt.adapter.socket_mode import SocketModeHandler +1. Open a direct message with your app or invite the bot `@first-bolt-app (local)` to a public channel. +2. Send "hello" to the current conversation and wait for a response. +3. Click the attached button labelled "Click Me" to post another reply. -# Initializes your app with your bot token and socket mode handler -app = App(token=os.environ.get("SLACK_BOT_TOKEN")) +After confirming the app responds, celebrate, then interrupt the process by pressing `CTRL+C` in the terminal to stop your app from running. -# Start your app -if __name__ == "__main__": - SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() -``` + + -Your tokens are enough to create your first Bolt app. Save your `app.py` file then on the command line run the following: +Navigate to your list of apps and [create a new Slack app](https://api.slack.com/apps/new) using the "from a manifest" option: -```script -python3 app.py -``` +1. Select the workspace to develop your app in. +2. Copy and paste the `manifest.json` file contents to create your app. +3. Confirm the app features and click "Create". -Your app should let you know that it's up and running. 🎉 +You'll then land on your app's **Basic Information** page, which is an overview of your app and which contains important credentials: ---- +![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") -### Setting up events {#setting-up-events} -Your app behaves similarly to people on your team — it can post messages, add emoji reactions, and listen and respond to events. +To listen for events happening in Slack (such as a new posted message) without opening a port or exposing an endpoint, we will use [Socket Mode](/concepts/socket-mode). This connection requires a specific app token: -To listen for events happening in a Slack workspace (like when a message is posted or when a reaction is posted to a message) you'll use the [Events API to subscribe to event types](https://docs.slack.dev/apis/events-api/). +1. On the **Basic Information** page, scroll to the **App-Level Tokens** section and click **Generate Token and Scopes**. +2. Name the token "Development" or something similar and add the `connections:write` scope, then click **Generate**. +3. Save the generated `xapp` token as an environment variable within your project: -For those just starting, we recommend using [Socket Mode](https://docs.slack.dev/apis/events-api/using-socket-mode). Socket Mode allows your app to use the Events API and interactive features without exposing a public HTTP Request URL. This can be helpful during development, or if you're receiving requests from behind a firewall. +```sh +$ export SLACK_APP_TOKEN= +``` -That being said, you're welcome to set up an app with a public HTTP Request URL. HTTP is more useful for apps being deployed to hosting environments to respond within a large corporate Slack workspaces/organization, or apps intended for distribution via the Slack Marketplace. +The above command works on Linux and macOS but [similar commands are available on Windows](https://superuser.com/questions/212150/how-to-set-env-variable-in-windows-cmd-line/212153#212153). -We've provided instructions for both ways in this guide. +:::warning[Keep it secret. Keep it safe.] -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; +Treat your tokens like a password and [keep it safe](https://docs.slack.dev/authentication/best-practices-for-security). Your app uses these to retrieve and send information to Slack. - - +::: -1. Head to your app's configuration page (click on the app [from your app settings page](https://api.slack.com/apps)). Navigate to **Socket Mode** on the left side menu and toggle to enable. +A bot token is also needed to interact with the Web API methods as your app's bot user. We can gather this as follows: -2. Go to **Basic Information** and scroll down under the App-Level Tokens section and click **Generate Token and Scopes** to generate an app token. Add the `connections:write` scope to this token and save the generated `xapp` token, we'll use that in just a moment. +1. Navigate to the **OAuth & Permissions** on the left sidebar and install your app to your workspace to generate a token. +2. After authorizing the installation, you'll return to the **OAuth & Permissions** page and find a **Bot User OAuth Token**: -3. Finally, it's time to tell Slack what events we'd like to listen for. Under **Event Subscriptions**, toggle the switch labeled **Enable Events**. +![OAuth Tokens](/img/boltpy/bot-token.png "Bot OAuth Token") -When an event occurs, Slack will send your app some information about the event, like the user that triggered it and the channel it occurred in. Your app will process the details and can respond accordingly. +3. Copy the bot token beginning with `xoxb` from the **OAuth & Permissions page** and then store it in a new environment variable: - - +```sh +$ export SLACK_BOT_TOKEN=xoxb- +``` -1. Go back to your app configuration page (click on the app [from your app management page](https://api.slack.com/apps)). Click **Event Subscriptions** on the left sidebar. Toggle the switch labeled **Enable Events**. +After saving tokens for the app you created, it is time to run it: -2. Add your Request URL. Slack will send HTTP POST requests corresponding to events to this [Request URL](https://docs.slack.dev/apis/events-api/#subscribing) endpoint. Bolt uses the `/slack/events` path to listen to all incoming requests (whether shortcuts, events, or interactivity payloads). When configuring your Request URL within your app configuration, you'll append `/slack/events`, e.g. `https:///slack/events`. 💡 As long as your Bolt app is still running, your URL should become verified. +```sh +$ python3 app.py +... +⚡️ Bolt app is running! +``` -:::tip +With the app running, you can test it out with the following steps in Slack: -For local development, you can use a proxy service like ngrok to create a public URL and tunnel requests to your development environment. Refer to [ngrok's getting started guide](https://ngrok.com/docs#getting-started-expose) on how to create this tunnel. And when you get to hosting your app, we've collected some of the most common hosting providers Slack developers use to host their apps [on our API site](https://docs.slack.dev/distribution/hosting-slack-apps/). +1. Open a direct message with your app or invite the bot `@BoltApp` to a public channel. +2. Send "hello" to the current conversation and wait for a response. +3. Click the attached button labelled "Click Me" to post another reply. -::: +After confirming the app responds, celebrate, then interrupt the process by pressing `CTRL+C` in the terminal to stop your app from running. -Navigate to **Event Subscriptions** on the left sidebar and toggle to enable. Under **Subscribe to Bot Events**, you can add events for your bot to respond to. There are four events related to messages: -- [`message.channels`](https://docs.slack.dev/reference/events/message.channels) listens for messages in public channels that your app is added to -- [`message.groups`](https://docs.slack.dev/reference/events/message.groups) listens for messages in 🔒 private channels that your app is added to -- [`message.im`](https://docs.slack.dev/reference/events/message.im) listens for messages in your app's DMs with users -- [`message.mpim`](https://docs.slack.dev/reference/events/message.mpim) listens for messages in multi-person DMs that your app is added to +## Updating the app -If you want your bot to listen to messages from everywhere it is added to, choose all four message events. After you’ve selected the events you want your bot to listen to, click the green **Save Changes** button. +At this point, you've successfully run the getting started Bolt for Python [app](https://github.com/slack-samples/bolt-python-getting-started-app)! ---- +The defaults included leave opportunities abound, so to personalize this app let's now edit the code to respond with a kind farewell. -### Listening and responding to a message {#listening-and-responding-to-a-message} -Your app is now ready for some logic. Let's start by using the `message()` method to attach a listener for messages. +#### Responding to a farewell -The following example listens and responds to all messages in channels/DMs where your app has been added that contain the word "hello": +Chat is a common thing apps do and responding to various types of messages can make conversations more interesting. - - +Using an editor of choice, open the `app.py` file and add the following import to the top of the file, and message listener after the "hello" handler: ```python -import os -from slack_bolt import App -from slack_bolt.adapter.socket_mode import SocketModeHandler - -# Initializes your app with your bot token and socket mode handler -app = App(token=os.environ.get("SLACK_BOT_TOKEN")) - -# Listens to incoming messages that contain "hello" -# To learn available listener arguments, -# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html -@app.message("hello") -def message_hello(message, say): - # say() sends a message to the channel where the event was triggered - say(f"Hey there <@{message['user']}>!") - -# Start your app -if __name__ == "__main__": - SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() -``` - - - +import random -```python -import os -from slack_bolt import App - -# Initializes your app with your bot token and signing secret -app = App( - token=os.environ.get("SLACK_BOT_TOKEN"), - signing_secret=os.environ.get("SLACK_SIGNING_SECRET") -) - -# Listens to incoming messages that contain "hello" -# To learn available listener arguments, -# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html -@app.message("hello") -def message_hello(message, say): - # say() sends a message to the channel where the event was triggered - say(f"Hey there <@{message['user']}>!") - -# Start your app -if __name__ == "__main__": - app.start(port=int(os.environ.get("PORT", 3000))) +@app.message("goodbye") +def message_goodbye(say): + responses = ["Adios", "Au revoir", "Farewell"] + parting = random.choice(responses) + say(f"{parting}!") ``` - - - -If you restart your app, so long as your bot user has been added to the channel/DM, when you send any message that contains "hello", it will respond. +Once the file is updated, save the changes and then we'll make sure those changes are being used. -This is a basic example, but it gives you a place to start customizing your app based on your own goals. Let's try something a little more interactive by sending a button rather than plain text. + + ---- +Run the following command and select the app created earlier to start, or restart, your app with the latest changes: -### Sending and responding to actions {#sending-and-responding-to-actions} +```sh +$ slack run +... +⚡️ Bolt app is running! +``` -To use features like buttons, select menus, datepickers, modals, and shortcuts, you’ll need to enable interactivity. Head over to **Interactivity & Shortcuts** in your app configuration. +After finding the above output appears, open Slack to perform these steps: - - +1. Return to the direct message or public channel with your bot. +2. Send "goodbye" to the conversation. +3. Receive a parting response from before and repeat "goodbye" to find another one. -With Socket Mode on, basic interactivity is enabled by default, so no further action is needed. +Your app can be stopped again by pressing `CTRL+C` in the terminal to end these chats. - + -Similar to events, you'll need to specify a URL for Slack to send the action (such as *user clicked a button*). Back on your app configuration page, click on **Interactivity & Shortcuts** on the left side. You'll see that there's another **Request URL** box. +Run the following command to start, or restart, your app with the latest changes: -:::tip +```sh +$ python3 app.py +... +⚡️ Bolt app is running! +``` -By default, Bolt is configured to use the same endpoint for interactive components that it uses for events, so use the same request URL as above (for example, `https://8e8ec2d7.ngrok.io/slack/events`). Press the **Save Changes** button in the lower right hand corner, and that's it. Your app is set up to handle interactivity! +After finding the above output appears, open Slack to perform these steps: -::: +1. Return to the direct message or public channel with your bot. +2. Send "goodbye" to the conversation. +3. Receive a parting response from before and repeat "goodbye" to find another one. + +Your app can be stopped again by pressing `CTRL+C` in the terminal to end these chats. -When interactivity is enabled, interactions with shortcuts, modals, or interactive components (such as buttons, select menus, and datepickers) will be sent to your app as events. +#### Customizing app settings -Now, let's go back to your app's code and add logic to handle those events: -- First, we'll send a message that contains an interactive component (in this case a button) -- Next, we'll listen for the action of a user clicking the button before responding +The created app will have some placeholder values and a small set of [scopes](https://docs.slack.dev/reference/scopes) to start, but we recommend exploring the customizations possible on app settings. -Below, the code from the last section is modified to send a message containing a button rather than just a string: + + - - - -```python -import os -from slack_bolt import App -from slack_bolt.adapter.socket_mode import SocketModeHandler - -# Initializes your app with your bot token and socket mode handler -app = App( - token=os.environ.get("SLACK_BOT_TOKEN"), - # signing_secret=os.environ.get("SLACK_SIGNING_SECRET") # not required for socket mode -) - -# Listens to incoming messages that contain "hello" -@app.message("hello") -def message_hello(message, say): - # say() sends a message to the channel where the event was triggered - say( - blocks=[ - { - "type": "section", - "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, - "accessory": { - "type": "button", - "text": {"type": "plain_text", "text": "Click Me"}, - "action_id": "button_click" - } - } - ], - text=f"Hey there <@{message['user']}>!" - ) - -# Start your app -if __name__ == "__main__": - SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() +Open app settings for your app with the following command: +```sh +$ slack app settings ``` - - +This will open the following page in a web browser: -```python -import os -from slack_bolt import App - -# Initializes your app with your bot token and signing secret -app = App( - token=os.environ.get("SLACK_BOT_TOKEN"), - signing_secret=os.environ.get("SLACK_SIGNING_SECRET") -) - -# Listens to incoming messages that contain "hello" -@app.message("hello") -def message_hello(message, say): - # say() sends a message to the channel where the event was triggered - say( - blocks=[ - { - "type": "section", - "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, - "accessory": { - "type": "button", - "text": {"type": "plain_text", "text": "Click Me"}, - "action_id": "button_click" - } - } - ], - text=f"Hey there <@{message['user']}>!" - ) - -# Start your app -if __name__ == "__main__": - app.start(port=int(os.environ.get("PORT", 3000))) -``` +![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") - - -The value inside of `say()` is now an object that contains an array of `blocks`. Blocks are the building components of a Slack message and can range from text to images to datepickers. In this case, your app will respond with a section block that includes a button as an accessory. Since we're using `blocks`, the `text` is a fallback for notifications and accessibility. - -You'll notice in the button `accessory` object, there is an `action_id`. This will act as a unique identifier for the button so your app can specify what action it wants to respond to. - -:::tip - -The [Block Kit Builder](https://app.slack.com/block-kit-builder) is an simple way to prototype your interactive messages. The builder lets you (or anyone on your team) mockup messages and generates the corresponding JSON that you can paste directly in your app. - -::: - -Now, if you restart your app and say "hello" in a channel your app is in, you'll see a message with a button. But if you click the button, nothing happens (*yet!*). - -Let's add a handler to send a followup message when someone clicks the button: + - - +Browse to https://api.slack.com/apps and select your app "Getting Started Bolt App" from the list. -```python -import os -from slack_bolt import App -from slack_bolt.adapter.socket_mode import SocketModeHandler - -# Initializes your app with your bot token and socket mode handler -app = App(token=os.environ.get("SLACK_BOT_TOKEN")) - -# Listens to incoming messages that contain "hello" -@app.message("hello") -def message_hello(message, say): - # say() sends a message to the channel where the event was triggered - say( - blocks=[ - { - "type": "section", - "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, - "accessory": { - "type": "button", - "text": {"type": "plain_text", "text": "Click Me"}, - "action_id": "button_click" - } - } - ], - text=f"Hey there <@{message['user']}>!" - ) - -@app.action("button_click") -def action_button_click(body, ack, say): - # Acknowledge the action - ack() - say(f"<@{body['user']['id']}> clicked the button") - -# Start your app -if __name__ == "__main__": - SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() -``` +This will open the following page: - - - -```python -import os -from slack_bolt import App - -# Initializes your app with your bot token and signing secret -app = App( - token=os.environ.get("SLACK_BOT_TOKEN"), - signing_secret=os.environ.get("SLACK_SIGNING_SECRET") -) - -# Listens to incoming messages that contain "hello" -@app.message("hello") -def message_hello(message, say): - # say() sends a message to the channel where the event was triggered - say( - blocks=[ - { - "type": "section", - "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, - "accessory": { - "type": "button", - "text": {"type": "plain_text", "text": "Click Me"}, - "action_id": "button_click" - } - } - ], - text=f"Hey there <@{message['user']}>!" - ) - -@app.action("button_click") -def action_button_click(body, ack, say): - # Acknowledge the action - ack() - say(f"<@{body['user']['id']}> clicked the button") - -# Start your app -if __name__ == "__main__": - app.start(port=int(os.environ.get("PORT", 3000))) -``` +![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") -You can see that we used `app.action()` to listen for the `action_id` that we named `button_click`. If you restart your app and click the button, you'll see a new message from your app that says you clicked the button. +On these pages you're free to make changes such as updating your app icon, configuring app features, and perhaps even distributing your app! ---- +## Next steps {#next-steps} -### Next steps {#next-steps} -You just built your first [Bolt for Python app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started)! 🎉 +Congrats once more on getting up and running with this quick start. -Now that you have a basic app up and running, you can start exploring how to make your Bolt app stand out. Here are some ideas about what to explore next: +:::info[Dive deeper] -* Read through the _Basic concepts_ to learn about the different methods and features your Bolt app has access to. +Follow along with the steps that went into making this app on the [building an app](/building-an-app) guide for an educational overview. -* Explore the different events your bot can listen to with the [`app.event()`](/concepts/event-listening) method. All of the events are listed [on the API site](https://docs.slack.dev/reference/events). +::: -* Bolt allows you to [call Web API methods](/concepts/web-api) with the client attached to your app. There are [over 220 methods](https://docs.slack.dev/reference/methods) on our API site. +You can now continue customizing your app with various features to make it right for whatever job's at hand. Here are some ideas about what to explore next: -* Learn more about the different token types [on our API site](https://docs.slack.dev/authentication/tokens). Your app may need different tokens depending on the actions you want it to perform. +- Explore the different events your bot can listen to with the [`app.event()`](/concepts/event-listening) method. All of the [events](https://docs.slack.dev/reference/events) are listed on the API docs site. +- Bolt allows you to call [Web API](/concepts/web-api) methods with the client attached to your app. There are [over 200 methods](https://docs.slack.dev/reference/methods) on the API docs site. +- Learn more about the different [token types](https://docs.slack.dev/authentication/tokens) and [authentication setups](/concepts/authenticating-oauth). Your app might need different tokens depending on the actions you want to perform or for installations to multiple workspaces. +- Receive events using HTTP for various deployment methods, such as deploying to [Heroku](/deployments/heroku) or [AWS Lambda](/deployments/aws-lambda). +- Read on [app design](https://docs.slack.dev/surfaces/app-design) and compose fancy messages with blocks using [Block Kit Builder](https://app.slack.com/block-kit-builder) to prototype messages. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 60f3eae81..0d80161e6 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -98,6 +98,7 @@ const config = { // switch to alucard when available in prism? theme: prismThemes.github, darkTheme: prismThemes.dracula, + additionalLanguages: ['bash'], }, codeblock: { showGithubLink: true, diff --git a/docs/sidebars.js b/docs/sidebars.js index 65c5e85f4..decb8cccb 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -7,13 +7,17 @@ const sidebars = { label: 'Bolt for Python', className: 'sidebar-title', }, + { + type: 'doc', + id: 'getting-started', + }, { type: 'html', value: '
    ' }, { type: 'category', label: 'Guides', collapsed: false, items: [ - "getting-started", + "building-an-app", { type: "category", label: "Slack API calls", From 227ca8e8936dffa93938591dabb14ddc79dc8a18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 00:21:59 +0000 Subject: [PATCH 119/282] chore(deps): bump the docusaurus group in /docs with 5 updates (#1325) --- docs/package-lock.json | 1072 +++++++++++++++------------------------- docs/package.json | 10 +- 2 files changed, 392 insertions(+), 690 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 4e7d7b445..6790558aa 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,9 +8,9 @@ "name": "website", "version": "2024.08.01", "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/plugin-client-redirects": "^3.8.0", - "@docusaurus/preset-classic": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/plugin-client-redirects": "^3.8.1", + "@docusaurus/preset-classic": "3.8.1", "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -19,8 +19,8 @@ "react-dom": "^19.1.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.8.0", - "@docusaurus/types": "3.8.0" + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/types": "3.8.1" }, "engines": { "node": ">=20.0" @@ -30,7 +30,6 @@ "version": "1.17.9", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.9.tgz", "integrity": "sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ==", - "license": "MIT", "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.9", "@algolia/autocomplete-shared": "1.17.9" @@ -40,7 +39,6 @@ "version": "1.17.9", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.9.tgz", "integrity": "sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ==", - "license": "MIT", "dependencies": { "@algolia/autocomplete-shared": "1.17.9" }, @@ -52,7 +50,6 @@ "version": "1.17.9", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.9.tgz", "integrity": "sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ==", - "license": "MIT", "dependencies": { "@algolia/autocomplete-shared": "1.17.9" }, @@ -65,106 +62,98 @@ "version": "1.17.9", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.9.tgz", "integrity": "sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ==", - "license": "MIT", "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "node_modules/@algolia/client-abtesting": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.25.0.tgz", - "integrity": "sha512-1pfQulNUYNf1Tk/svbfjfkLBS36zsuph6m+B6gDkPEivFmso/XnRgwDvjAx80WNtiHnmeNjIXdF7Gos8+OLHqQ==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.30.0.tgz", + "integrity": "sha512-Q3OQXYlTNqVUN/V1qXX8VIzQbLjP3yrRBO9m6NRe1CBALmoGHh9JrYosEGvfior28+DjqqU3Q+nzCSuf/bX0Gw==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.25.0.tgz", - "integrity": "sha512-AFbG6VDJX/o2vDd9hqncj1B6B4Tulk61mY0pzTtzKClyTDlNP0xaUiEKhl6E7KO9I/x0FJF5tDCm0Hn6v5x18A==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.30.0.tgz", + "integrity": "sha512-/b+SAfHjYjx/ZVeVReCKTTnFAiZWOyvYLrkYpeNMraMT6akYRR8eC1AvFcvR60GLG/jytxcJAp42G8nN5SdcLg==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.25.0.tgz", - "integrity": "sha512-il1zS/+Rc6la6RaCdSZ2YbJnkQC6W1wiBO8+SH+DE6CPMWBU6iDVzH0sCKSAtMWl9WBxoN6MhNjGBnCv9Yy2bA==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.30.0.tgz", + "integrity": "sha512-tbUgvkp2d20mHPbM0+NPbLg6SzkUh0lADUUjzNCF+HiPkjFRaIW3NGMlESKw5ia4Oz6ZvFzyREquUX6rdkdJcQ==", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.25.0.tgz", - "integrity": "sha512-blbjrUH1siZNfyCGeq0iLQu00w3a4fBXm0WRIM0V8alcAPo7rWjLbMJMrfBtzL9X5ic6wgxVpDADXduGtdrnkw==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.30.0.tgz", + "integrity": "sha512-caXuZqJK761m32KoEAEkjkE2WF/zYg1McuGesWXiLSgfxwZZIAf+DljpiSToBUXhoPesvjcLtINyYUzbkwE0iw==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.25.0.tgz", - "integrity": "sha512-aywoEuu1NxChBcHZ1pWaat0Plw7A8jDMwjgRJ00Mcl7wGlwuPt5dJ/LTNcg3McsEUbs2MBNmw0ignXBw9Tbgow==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.30.0.tgz", + "integrity": "sha512-7K6P7TRBHLX1zTmwKDrIeBSgUidmbj6u3UW/AfroLRDGf9oZFytPKU49wg28lz/yulPuHY0nZqiwbyAxq9V17w==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.25.0.tgz", - "integrity": "sha512-a/W2z6XWKjKjIW1QQQV8PTTj1TXtaKx79uR3NGBdBdGvVdt24KzGAaN7sCr5oP8DW4D3cJt44wp2OY/fZcPAVA==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.30.0.tgz", + "integrity": "sha512-WMjWuBjYxJheRt7Ec5BFr33k3cV0mq2WzmH9aBf5W4TT8kUp34x91VRsYVaWOBRlxIXI8o/WbhleqSngiuqjLA==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.25.0.tgz", - "integrity": "sha512-9rUYcMIBOrCtYiLX49djyzxqdK9Dya/6Z/8sebPn94BekT+KLOpaZCuc6s0Fpfq7nx5J6YY5LIVFQrtioK9u0g==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.30.0.tgz", + "integrity": "sha512-puc1/LREfSqzgmrOFMY5L/aWmhYOlJ0TTpa245C0ZNMKEkdOkcimFbXTXQ8lZhzh+rlyFgR7cQGNtXJ5H0XgZg==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" @@ -173,85 +162,78 @@ "node_modules/@algolia/events": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", - "license": "MIT" + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "node_modules/@algolia/ingestion": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.25.0.tgz", - "integrity": "sha512-jJeH/Hk+k17Vkokf02lkfYE4A+EJX+UgnMhTLR/Mb+d1ya5WhE+po8p5a/Nxb6lo9OLCRl6w3Hmk1TX1e9gVbQ==", - "license": "MIT", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.30.0.tgz", + "integrity": "sha512-NfqiIKVgGKTLr6T9F81oqB39pPiEtILTy0z8ujxPKg2rCvI/qQeDqDWFBmQPElCfUTU6kk67QAgMkQ7T6fE+gg==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.25.0.tgz", - "integrity": "sha512-Ls3i1AehJ0C6xaHe7kK9vPmzImOn5zBg7Kzj8tRYIcmCWVyuuFwCIsbuIIz/qzUf1FPSWmw0TZrGeTumk2fqXg==", - "license": "MIT", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.30.0.tgz", + "integrity": "sha512-/eeM3aqLKro5KBZw0W30iIA6afkGa+bcpvEM0NDa92m5t3vil4LOmJI9FkgzfmSkF4368z/SZMOTPShYcaVXjA==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.25.0.tgz", - "integrity": "sha512-79sMdHpiRLXVxSjgw7Pt4R1aNUHxFLHiaTDnN2MQjHwJ1+o3wSseb55T9VXU4kqy3m7TUme3pyRhLk5ip/S4Mw==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.30.0.tgz", + "integrity": "sha512-iWeAUWqw+xT+2IyUyTqnHCK+cyCKYV5+B6PXKdagc9GJJn6IaPs8vovwoC0Za5vKCje/aXQ24a2Z1pKpc/tdHg==", "dependencies": { - "@algolia/client-common": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "@algolia/client-common": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.25.0.tgz", - "integrity": "sha512-JLaF23p1SOPBmfEqozUAgKHQrGl3z/Z5RHbggBu6s07QqXXcazEsub5VLonCxGVqTv6a61AAPr8J1G5HgGGjEw==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.30.0.tgz", + "integrity": "sha512-alo3ly0tdNLjfMSPz9dmNwYUFHx7guaz5dTGlIzVGnOiwLgIoM6NgA+MJLMcH6e1S7OpmE2AxOy78svlhst2tQ==", "dependencies": { - "@algolia/client-common": "5.25.0" + "@algolia/client-common": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.25.0.tgz", - "integrity": "sha512-rtzXwqzFi1edkOF6sXxq+HhmRKDy7tz84u0o5t1fXwz0cwx+cjpmxu/6OQKTdOJFS92JUYHsG51Iunie7xbqfQ==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.30.0.tgz", + "integrity": "sha512-WOnTYUIY2InllHBy6HHMpGIOo7Or4xhYUx/jkoSK/kPIa1BRoFEHqa8v4pbKHtoG7oLvM2UAsylSnjVpIhGZXg==", "dependencies": { - "@algolia/client-common": "5.25.0" + "@algolia/client-common": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.25.0.tgz", - "integrity": "sha512-ZO0UKvDyEFvyeJQX0gmZDQEvhLZ2X10K+ps6hViMo1HgE2V8em00SwNsQ+7E/52a+YiBkVWX61pJJJE44juDMQ==", - "license": "MIT", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.30.0.tgz", + "integrity": "sha512-uSTUh9fxeHde1c7KhvZKUrivk90sdiDftC+rSKNFKKEU9TiIKAGA7B2oKC+AoMCqMymot1vW9SGbeESQPTZd0w==", "dependencies": { - "@algolia/client-common": "5.25.0" + "@algolia/client-common": "5.30.0" }, "engines": { "node": ">= 14.0.0" @@ -731,7 +713,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1379,7 +1360,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.27.1.tgz", "integrity": "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==", - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1499,7 +1479,6 @@ "version": "7.27.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.4.tgz", "integrity": "sha512-D68nR5zxU64EUzV8i7T3R5XP0Xhrou/amNnddsRQssx6GrTLdZl1rLxyjtVZBd+v/NVX4AbTPOB5aU8thAZV1A==", - "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", @@ -1519,7 +1498,6 @@ "version": "0.11.1", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", - "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3", "core-js-compat": "^3.40.0" @@ -1532,7 +1510,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -1838,10 +1815,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.4.tgz", - "integrity": "sha512-H7QhL0ucCGOObsUETNbB2PuzF4gAvN8p32P6r91bX7M/hk4bx+3yz2hTwHL9d/Efzwu1upeb4/cd7oSxCzup3w==", - "license": "MIT", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.6.tgz", + "integrity": "sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==", "dependencies": { "core-js-pure": "^3.30.2" }, @@ -1918,7 +1894,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "engines": { "node": ">=18" }, @@ -1941,7 +1916,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" } @@ -1960,7 +1934,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "engines": { "node": ">=18" }, @@ -1983,7 +1956,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "@csstools/color-helpers": "^5.0.2", "@csstools/css-calc": "^2.1.4" @@ -2010,7 +1982,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "engines": { "node": ">=18" }, @@ -2032,7 +2003,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "engines": { "node": ">=18" } @@ -2051,7 +2021,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "engines": { "node": ">=18" }, @@ -2061,9 +2030,9 @@ } }, "node_modules/@csstools/postcss-cascade-layers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.1.tgz", - "integrity": "sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz", + "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==", "funding": [ { "type": "github", @@ -2074,7 +2043,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0" @@ -2100,7 +2068,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -2112,7 +2079,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2135,7 +2101,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2164,7 +2129,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2193,7 +2157,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2222,7 +2185,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", @@ -2250,7 +2212,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2277,7 +2238,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" @@ -2303,7 +2263,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2330,7 +2289,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2359,7 +2317,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2388,7 +2345,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", @@ -2415,7 +2371,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -2424,9 +2379,9 @@ } }, "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.1.tgz", - "integrity": "sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", + "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", "funding": [ { "type": "github", @@ -2437,7 +2392,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0" @@ -2463,7 +2417,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -2475,7 +2428,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2498,7 +2450,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", @@ -2526,7 +2477,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -2548,7 +2498,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -2570,7 +2519,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -2592,7 +2540,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -2617,7 +2564,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-tokenizer": "^3.0.4", "@csstools/utilities": "^2.0.0" @@ -2643,7 +2589,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2671,7 +2616,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", @@ -2698,7 +2642,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" @@ -2724,7 +2667,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -2749,7 +2691,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2778,7 +2719,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -2803,7 +2743,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2830,7 +2769,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2859,7 +2797,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -2874,7 +2811,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2897,7 +2833,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2924,7 +2859,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-parser-algorithms": "^3.0.5", @@ -2951,7 +2885,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/color-helpers": "^5.0.2", "postcss-value-parser": "^4.2.0" @@ -2977,7 +2910,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-parser-algorithms": "^3.0.5", @@ -3004,7 +2936,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -3026,7 +2957,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -3046,14 +2976,12 @@ "node_modules/@docsearch/css": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.9.0.tgz", - "integrity": "sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA==", - "license": "MIT" + "integrity": "sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA==" }, "node_modules/@docsearch/react": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.9.0.tgz", "integrity": "sha512-mb5FOZYZIkRQ6s/NWnM98k879vu5pscWqTLubLFBO87igYYT4VzVazh4h5o/zCvTIZgEt3PvsCOMOswOUo9yHQ==", - "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "1.17.9", "@algolia/autocomplete-preset-algolia": "1.17.9", @@ -3082,10 +3010,9 @@ } }, "node_modules/@docusaurus/babel": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.0.tgz", - "integrity": "sha512-9EJwSgS6TgB8IzGk1L8XddJLhZod8fXT4ULYMx6SKqyCBqCFpVCEjR/hNXXhnmtVM2irDuzYoVLGWv7srG/VOA==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.1.tgz", + "integrity": "sha512-3brkJrml8vUbn9aeoZUlJfsI/GqyFcDgQJwQkmBtclJgWDEQBKKeagZfOgx0WfUQhagL1sQLNW0iBdxnI863Uw==", "dependencies": { "@babel/core": "^7.25.9", "@babel/generator": "^7.25.9", @@ -3097,8 +3024,8 @@ "@babel/runtime": "^7.25.9", "@babel/runtime-corejs3": "^7.25.9", "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.8.0", - "@docusaurus/utils": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3108,30 +3035,29 @@ } }, "node_modules/@docusaurus/bundler": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.0.tgz", - "integrity": "sha512-Rq4Z/MSeAHjVzBLirLeMcjLIAQy92pF1OI+2rmt18fSlMARfTGLWRE8Vb+ljQPTOSfJxwDYSzsK6i7XloD2rNA==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.1.tgz", + "integrity": "sha512-/z4V0FRoQ0GuSLToNjOSGsk6m2lQUG4FRn8goOVoZSRsTrU8YR2aJacX5K3RG18EaX9b+52pN4m1sL3MQZVsQA==", "dependencies": { "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.8.0", - "@docusaurus/cssnano-preset": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", + "@docusaurus/babel": "3.8.1", + "@docusaurus/cssnano-preset": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", "babel-loader": "^9.2.1", - "clean-css": "^5.3.2", + "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.8.1", + "css-loader": "^6.11.0", "css-minimizer-webpack-plugin": "^5.0.1", "cssnano": "^6.1.2", "file-loader": "^6.2.0", "html-minifier-terser": "^7.2.0", - "mini-css-extract-plugin": "^2.9.1", + "mini-css-extract-plugin": "^2.9.2", "null-loader": "^4.0.1", - "postcss": "^8.4.26", - "postcss-loader": "^7.3.3", - "postcss-preset-env": "^10.1.0", + "postcss": "^8.5.4", + "postcss-loader": "^7.3.4", + "postcss-preset-env": "^10.2.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "url-loader": "^4.1.1", @@ -3151,18 +3077,17 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.0.tgz", - "integrity": "sha512-c7u6zFELmSGPEP9WSubhVDjgnpiHgDqMh1qVdCB7rTflh4Jx0msTYmMiO91Ez0KtHj4sIsDsASnjwfJ2IZp3Vw==", - "license": "MIT", - "dependencies": { - "@docusaurus/babel": "3.8.0", - "@docusaurus/bundler": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.1.tgz", + "integrity": "sha512-ENB01IyQSqI2FLtOzqSI3qxG2B/jP4gQPahl2C3XReiLebcVh5B5cB9KYFvdoOqOWPyr5gXK4sjgTKv7peXCrA==", + "dependencies": { + "@docusaurus/babel": "3.8.1", + "@docusaurus/bundler": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -3212,13 +3137,12 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.0.tgz", - "integrity": "sha512-UJ4hAS2T0R4WNy+phwVff2Q0L5+RXW9cwlH6AEphHR5qw3m/yacfWcSK7ort2pMMbDn8uGrD38BTm4oLkuuNoQ==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.1.tgz", + "integrity": "sha512-G7WyR2N6SpyUotqhGznERBK+x84uyhfMQM2MmDLs88bw4Flom6TY46HzkRkSEzaP9j80MbTN8naiL1fR17WQug==", "dependencies": { "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.4.38", + "postcss": "^8.5.4", "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" }, @@ -3227,10 +3151,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.0.tgz", - "integrity": "sha512-7eEMaFIam5Q+v8XwGqF/n0ZoCld4hV4eCCgQkfcN9Mq5inoZa6PHHW9Wu6lmgzoK5Kx3keEeABcO2SxwraoPDQ==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.1.tgz", + "integrity": "sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -3240,14 +3163,13 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.0.tgz", - "integrity": "sha512-mDPSzssRnpjSdCGuv7z2EIAnPS1MHuZGTaRLwPn4oQwszu4afjWZ/60sfKjTnjBjI8Vl4OgJl2vMmfmiNDX4Ng==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.1.tgz", + "integrity": "sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w==", "dependencies": { - "@docusaurus/logger": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -3279,12 +3201,11 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.0.tgz", - "integrity": "sha512-/uMb4Ipt5J/QnD13MpnoC/A4EYAe6DKNWqTWLlGrqsPJwJv73vSwkA25xnYunwfqWk0FlUQfGv/Swdh5eCCg7g==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.1.tgz", + "integrity": "sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg==", "dependencies": { - "@docusaurus/types": "3.8.0", + "@docusaurus/types": "3.8.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3298,16 +3219,15 @@ } }, "node_modules/@docusaurus/plugin-client-redirects": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.8.0.tgz", - "integrity": "sha512-J8f5qzAlO61BnG1I91+N5WH1b/lPWqn6ifTxf/Bluz9JVe1bhFNSl0yW03p+Ff3AFOINDy2ofX70al9nOnOLyw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.8.1.tgz", + "integrity": "sha512-F+86R7PBn6VNgy/Ux8w3ZRypJGJEzksbejQKlbTC8u6uhBUhfdXWkDp6qdOisIoW0buY5nLqucvZt1zNJzhJhA==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "eta": "^2.2.0", "fs-extra": "^11.1.1", "lodash": "^4.17.21", @@ -3322,19 +3242,18 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.8.0.tgz", - "integrity": "sha512-0SlOTd9R55WEr1GgIXu+hhTT0hzARYx3zIScA5IzpdekZQesI/hKEa5LPHBd415fLkWMjdD59TaW/3qQKpJ0Lg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/theme-common": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.8.1.tgz", + "integrity": "sha512-vNTpMmlvNP9n3hGEcgPaXyvTljanAKIUkuG9URQ1DeuDup0OR7Ltvoc8yrmH+iMZJbcQGhUJF+WjHLwuk8HSdw==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -3356,20 +3275,19 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.0.tgz", - "integrity": "sha512-fRDMFLbUN6eVRXcjP8s3Y7HpAt9pzPYh1F/7KKXOCxvJhjjCtbon4VJW0WndEPInVz4t8QUXn5QZkU2tGVCE2g==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/module-type-aliases": "3.8.0", - "@docusaurus/theme-common": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.1.tgz", + "integrity": "sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -3389,16 +3307,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.8.0.tgz", - "integrity": "sha512-39EDx2y1GA0Pxfion5tQZLNJxL4gq6susd1xzetVBjVIQtwpCdyloOfQBAgX0FylqQxfJrYqL0DIUuq7rd7uBw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.8.1.tgz", + "integrity": "sha512-a+V6MS2cIu37E/m7nDJn3dcxpvXb6TvgdNI22vJX8iUTp8eoMoPa0VArEbWvCxMY/xdC26WzNv4wZ6y0iIni/w==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -3412,14 +3329,14 @@ } }, "node_modules/@docusaurus/plugin-css-cascade-layers": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.8.0.tgz", - "integrity": "sha512-/VBTNymPIxQB8oA3ZQ4GFFRYdH4ZxDRRBECxyjRyv486mfUPXfcdk+im4S5mKWa6EK2JzBz95IH/Wu0qQgJ5yQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.8.1.tgz", + "integrity": "sha512-VQ47xRxfNKjHS5ItzaVXpxeTm7/wJLFMOPo1BkmoMG4Cuz4nuI+Hs62+RMk1OqVog68Swz66xVPK8g9XTrBKRw==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3427,14 +3344,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.8.0.tgz", - "integrity": "sha512-teonJvJsDB9o2OnG6ifbhblg/PXzZvpUKHFgD8dOL1UJ58u0lk8o0ZOkvaYEBa9nDgqzoWrRk9w+e3qaG2mOhQ==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.8.1.tgz", + "integrity": "sha512-nT3lN7TV5bi5hKMB7FK8gCffFTBSsBsAfV84/v293qAmnHOyg1nr9okEw8AiwcO3bl9vije5nsUvP0aRl2lpaw==", "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", "fs-extra": "^11.1.1", "react-json-view-lite": "^2.3.0", "tslib": "^2.6.0" @@ -3448,14 +3364,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.8.0.tgz", - "integrity": "sha512-aKKa7Q8+3xRSRESipNvlFgNp3FNPELKhuo48Cg/svQbGNwidSHbZT03JqbW4cBaQnyyVchO1ttk+kJ5VC9Gx0w==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.8.1.tgz", + "integrity": "sha512-Hrb/PurOJsmwHAsfMDH6oVpahkEGsx7F8CWMjyP/dw1qjqmdS9rcV1nYCGlM8nOtD3Wk/eaThzUB5TSZsGz+7Q==", "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3467,14 +3382,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.8.0.tgz", - "integrity": "sha512-ugQYMGF4BjbAW/JIBtVcp+9eZEgT9HRdvdcDudl5rywNPBA0lct+lXMG3r17s02rrhInMpjMahN3Yc9Cb3H5/g==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.8.1.tgz", + "integrity": "sha512-tKE8j1cEZCh8KZa4aa80zpSTxsC2/ZYqjx6AAfd8uA8VHZVw79+7OTEP2PoWi0uL5/1Is0LF5Vwxd+1fz5HlKg==", "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -3487,14 +3401,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.8.0.tgz", - "integrity": "sha512-9juRWxbwZD3SV02Jd9QB6yeN7eu+7T4zB0bvJLcVQwi+am51wAxn2CwbdL0YCCX+9OfiXbADE8D8Q65Hbopu/w==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.8.1.tgz", + "integrity": "sha512-iqe3XKITBquZq+6UAXdb1vI0fPY5iIOitVjPQ581R1ZKpHr0qe+V6gVOrrcOHixPDD/BUKdYwkxFjpNiEN+vBw==", "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3506,17 +3419,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.8.0.tgz", - "integrity": "sha512-fGpOIyJvNiuAb90nSJ2Gfy/hUOaDu6826e5w5UxPmbpCIc7KlBHNAZ5g4L4ZuHhc4hdfq4mzVBsQSnne+8Ze1g==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.8.1.tgz", + "integrity": "sha512-+9YV/7VLbGTq8qNkjiugIelmfUEVkTyLe6X8bWq7K5qPvGXAjno27QAfFq63mYfFFbJc7z+pudL63acprbqGzw==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -3530,15 +3442,14 @@ } }, "node_modules/@docusaurus/plugin-svgr": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.8.0.tgz", - "integrity": "sha512-kEDyry+4OMz6BWLG/lEqrNsL/w818bywK70N1gytViw4m9iAmoxCUT7Ri9Dgs7xUdzCHJ3OujolEmD88Wy44OA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.8.1.tgz", + "integrity": "sha512-rW0LWMDsdlsgowVwqiMb/7tANDodpy1wWPwCcamvhY7OECReN3feoFwLjd/U4tKjNY3encj0AJSTxJA+Fpe+Gw==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", @@ -3553,26 +3464,25 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.8.0.tgz", - "integrity": "sha512-qOu6tQDOWv+rpTlKu+eJATCJVGnABpRCPuqf7LbEaQ1mNY//N/P8cHQwkpAU+aweQfarcZ0XfwCqRHJfjeSV/g==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/plugin-content-blog": "3.8.0", - "@docusaurus/plugin-content-docs": "3.8.0", - "@docusaurus/plugin-content-pages": "3.8.0", - "@docusaurus/plugin-css-cascade-layers": "3.8.0", - "@docusaurus/plugin-debug": "3.8.0", - "@docusaurus/plugin-google-analytics": "3.8.0", - "@docusaurus/plugin-google-gtag": "3.8.0", - "@docusaurus/plugin-google-tag-manager": "3.8.0", - "@docusaurus/plugin-sitemap": "3.8.0", - "@docusaurus/plugin-svgr": "3.8.0", - "@docusaurus/theme-classic": "3.8.0", - "@docusaurus/theme-common": "3.8.0", - "@docusaurus/theme-search-algolia": "3.8.0", - "@docusaurus/types": "3.8.0" + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.8.1.tgz", + "integrity": "sha512-yJSjYNHXD8POMGc2mKQuj3ApPrN+eG0rO1UPgSx7jySpYU+n4WjBikbrA2ue5ad9A7aouEtMWUoiSRXTH/g7KQ==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/plugin-content-blog": "3.8.1", + "@docusaurus/plugin-content-docs": "3.8.1", + "@docusaurus/plugin-content-pages": "3.8.1", + "@docusaurus/plugin-css-cascade-layers": "3.8.1", + "@docusaurus/plugin-debug": "3.8.1", + "@docusaurus/plugin-google-analytics": "3.8.1", + "@docusaurus/plugin-google-gtag": "3.8.1", + "@docusaurus/plugin-google-tag-manager": "3.8.1", + "@docusaurus/plugin-sitemap": "3.8.1", + "@docusaurus/plugin-svgr": "3.8.1", + "@docusaurus/theme-classic": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/theme-search-algolia": "3.8.1", + "@docusaurus/types": "3.8.1" }, "engines": { "node": ">=18.0" @@ -3583,31 +3493,30 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.8.0.tgz", - "integrity": "sha512-nQWFiD5ZjoT76OaELt2n33P3WVuuCz8Dt5KFRP2fCBo2r9JCLsp2GJjZpnaG24LZ5/arRjv4VqWKgpK0/YLt7g==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/module-type-aliases": "3.8.0", - "@docusaurus/plugin-content-blog": "3.8.0", - "@docusaurus/plugin-content-docs": "3.8.0", - "@docusaurus/plugin-content-pages": "3.8.0", - "@docusaurus/theme-common": "3.8.0", - "@docusaurus/theme-translations": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.8.1.tgz", + "integrity": "sha512-bqDUCNqXeYypMCsE1VcTXSI1QuO4KXfx8Cvl6rYfY0bhhqN6d2WZlRkyLg/p6pm+DzvanqHOyYlqdPyP0iz+iw==", + "dependencies": { + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/plugin-content-blog": "3.8.1", + "@docusaurus/plugin-content-docs": "3.8.1", + "@docusaurus/plugin-content-pages": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/theme-translations": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", - "postcss": "^8.4.26", + "postcss": "^8.5.4", "prism-react-renderer": "^2.3.0", "prismjs": "^1.29.0", "react-router-dom": "^5.3.4", @@ -3624,15 +3533,14 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.0.tgz", - "integrity": "sha512-YqV2vAWpXGLA+A3PMLrOMtqgTHJLDcT+1Caa6RF7N4/IWgrevy5diY8oIHFkXR/eybjcrFFjUPrHif8gSGs3Tw==", - "license": "MIT", - "dependencies": { - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/module-type-aliases": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.1.tgz", + "integrity": "sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw==", + "dependencies": { + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3652,19 +3560,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.0.tgz", - "integrity": "sha512-GBZ5UOcPgiu6nUw153+0+PNWvFKweSnvKIL6Rp04H9olKb475jfKjAwCCtju5D2xs5qXHvCMvzWOg5o9f6DtuQ==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.1.tgz", + "integrity": "sha512-NBFH5rZVQRAQM087aYSRKQ9yGEK9eHd+xOxQjqNpxMiV85OhJDD4ZGz6YJIod26Fbooy54UWVdzNU0TFeUUUzQ==", "dependencies": { "@docsearch/react": "^3.9.0", - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/plugin-content-docs": "3.8.0", - "@docusaurus/theme-common": "3.8.0", - "@docusaurus/theme-translations": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/plugin-content-docs": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/theme-translations": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "algoliasearch": "^5.17.1", "algoliasearch-helper": "^3.22.6", "clsx": "^2.0.0", @@ -3683,10 +3590,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.0.tgz", - "integrity": "sha512-1DTy/snHicgkCkryWq54fZvsAglTdjTx4qjOXgqnXJ+DIty1B+aPQrAVUu8LiM+6BiILfmNxYsxhKTj+BS3PZg==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.1.tgz", + "integrity": "sha512-OTp6eebuMcf2rJt4bqnvuwmm3NVXfzfYejL+u/Y1qwKhZPrjPoKWfk1CbOP5xH5ZOPkiAsx4dHdQBRJszK3z2g==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3696,10 +3602,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.0.tgz", - "integrity": "sha512-RDEClpwNxZq02c+JlaKLWoS13qwWhjcNsi2wG1UpzmEnuti/z1Wx4SGpqbUqRPNSd8QWWePR8Cb7DvG0VN/TtA==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.1.tgz", + "integrity": "sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg==", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -3731,14 +3636,13 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.0.tgz", - "integrity": "sha512-2wvtG28ALCN/A1WCSLxPASFBFzXCnP0YKCAFIPcvEb6imNu1wg7ni/Svcp71b3Z2FaOFFIv4Hq+j4gD7gA0yfQ==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.1.tgz", + "integrity": "sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ==", "dependencies": { - "@docusaurus/logger": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils-common": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "escape-string-regexp": "^4.0.0", "execa": "5.1.1", "file-loader": "^6.2.0", @@ -3763,12 +3667,11 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.0.tgz", - "integrity": "sha512-3TGF+wVTGgQ3pAc9+5jVchES4uXUAhAt9pwv7uws4mVOxL4alvU3ue/EZ+R4XuGk94pDy7CNXjRXpPjlfZXQfw==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.1.tgz", + "integrity": "sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg==", "dependencies": { - "@docusaurus/types": "3.8.0", + "@docusaurus/types": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3776,14 +3679,13 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.0.tgz", - "integrity": "sha512-MrnEbkigr54HkdFeg8e4FKc4EF+E9dlVwsY3XQZsNkbv3MKZnbHQ5LsNJDIKDROFe8PBf5C4qCAg5TPBpsjrjg==", - "license": "MIT", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.1.tgz", + "integrity": "sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA==", "dependencies": { - "@docusaurus/logger": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -3811,7 +3713,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -3823,7 +3724,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -4044,8 +3944,7 @@ "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "license": "MIT" + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" }, "node_modules/@sindresorhus/is": { "version": "4.6.0", @@ -4074,7 +3973,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -4090,7 +3988,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -4106,7 +4003,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -4122,7 +4018,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -4138,7 +4033,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -4154,7 +4048,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -4170,7 +4063,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -4186,7 +4078,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -4202,7 +4093,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "license": "MIT", "dependencies": { "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", @@ -4228,7 +4118,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -4248,7 +4137,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "license": "MIT", "dependencies": { "@babel/types": "^7.21.3", "entities": "^4.4.0" @@ -4265,7 +4153,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -4287,7 +4174,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", - "license": "MIT", "dependencies": { "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", @@ -4308,7 +4194,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", - "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-transform-react-constant-elements": "^7.21.3", @@ -4471,8 +4356,7 @@ "node_modules/@types/gtag.js": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", - "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==", - "license": "MIT" + "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" }, "node_modules/@types/hast": { "version": "3.0.4", @@ -4517,14 +4401,12 @@ "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT" + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -4533,7 +4415,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -4655,7 +4536,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -4717,7 +4597,6 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -4725,8 +4604,7 @@ "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", @@ -5016,34 +4894,32 @@ } }, "node_modules/algoliasearch": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.25.0.tgz", - "integrity": "sha512-n73BVorL4HIwKlfJKb4SEzAYkR3Buwfwbh+MYxg2mloFph2fFGV58E90QTzdbfzWrLn4HE5Czx/WTjI8fcHaMg==", - "license": "MIT", - "dependencies": { - "@algolia/client-abtesting": "5.25.0", - "@algolia/client-analytics": "5.25.0", - "@algolia/client-common": "5.25.0", - "@algolia/client-insights": "5.25.0", - "@algolia/client-personalization": "5.25.0", - "@algolia/client-query-suggestions": "5.25.0", - "@algolia/client-search": "5.25.0", - "@algolia/ingestion": "1.25.0", - "@algolia/monitoring": "1.25.0", - "@algolia/recommend": "5.25.0", - "@algolia/requester-browser-xhr": "5.25.0", - "@algolia/requester-fetch": "5.25.0", - "@algolia/requester-node-http": "5.25.0" + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.30.0.tgz", + "integrity": "sha512-ILSdPX4je0n5WUKD34TMe57/eqiXUzCIjAsdtLQYhomqOjTtFUg1s6dE7kUegc4Mc43Xr7IXYlMutU9HPiYfdw==", + "dependencies": { + "@algolia/client-abtesting": "5.30.0", + "@algolia/client-analytics": "5.30.0", + "@algolia/client-common": "5.30.0", + "@algolia/client-insights": "5.30.0", + "@algolia/client-personalization": "5.30.0", + "@algolia/client-query-suggestions": "5.30.0", + "@algolia/client-search": "5.30.0", + "@algolia/ingestion": "1.30.0", + "@algolia/monitoring": "1.30.0", + "@algolia/recommend": "5.30.0", + "@algolia/requester-browser-xhr": "5.30.0", + "@algolia/requester-fetch": "5.30.0", + "@algolia/requester-node-http": "5.30.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.25.0.tgz", - "integrity": "sha512-vQoK43U6HXA9/euCqLjvyNdM4G2Fiu/VFp4ae0Gau9sZeIKBPvUPnXfLYAe65Bg7PFuw03coeu5K6lTPSXRObw==", - "license": "MIT", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.26.0.tgz", + "integrity": "sha512-Rv2x3GXleQ3ygwhkhJubhhYGsICmShLAiqtUuJTUkr9uOCOXyF2E71LVT4XDnVffbknv8XgScP4U0Oxtgm+hIw==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -5084,7 +4960,6 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -5099,7 +4974,6 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -5158,8 +5032,7 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/argparse": { "version": "2.0.1", @@ -5206,7 +5079,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", @@ -5229,7 +5101,6 @@ "version": "9.2.1", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", - "license": "MIT", "dependencies": { "find-cache-dir": "^4.0.0", "schema-utils": "^4.0.0" @@ -5246,7 +5117,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "license": "MIT", "dependencies": { "object.assign": "^4.1.0" } @@ -5518,7 +5388,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -5594,7 +5463,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "license": "MIT", "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", @@ -5695,7 +5563,6 @@ "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "license": "MIT", "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -5716,7 +5583,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", @@ -5902,8 +5768,7 @@ "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "license": "MIT" + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" }, "node_modules/colorette": { "version": "2.0.20", @@ -5939,8 +5804,7 @@ "node_modules/common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "license": "ISC" + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" }, "node_modules/compressible": { "version": "2.0.18", @@ -6052,7 +5916,6 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -6099,7 +5962,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -6111,7 +5973,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "license": "MIT", "dependencies": { "fast-glob": "^3.2.11", "glob-parent": "^6.0.1", @@ -6135,7 +5996,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -6147,7 +6007,6 @@ "version": "13.2.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "license": "MIT", "dependencies": { "dir-glob": "^3.0.1", "fast-glob": "^3.3.0", @@ -6166,7 +6025,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -6199,11 +6057,10 @@ } }, "node_modules/core-js-pure": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz", - "integrity": "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ==", + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.43.0.tgz", + "integrity": "sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA==", "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -6295,7 +6152,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -6310,7 +6166,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -6323,7 +6178,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", - "license": "ISC", "engines": { "node": "^14 || ^16 || >=18" }, @@ -6345,7 +6199,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0", @@ -6372,7 +6225,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -6384,7 +6236,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -6397,7 +6248,6 @@ "version": "6.11.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -6432,7 +6282,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "cssnano": "^6.0.1", @@ -6486,7 +6335,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -6533,9 +6381,9 @@ } }, "node_modules/cssdb": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.3.0.tgz", - "integrity": "sha512-c7bmItIg38DgGjSwDPZOYF/2o0QU/sSgkWOMyl8votOfgFuyiFKWPesmCGEsrGLxEA9uL540cp8LdaGEjUGsZQ==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.3.1.tgz", + "integrity": "sha512-XnDRQMXucLueX92yDe0LPKupXetWoFOgawr4O4X41l5TltgK2NVbJJVDnnOywDYfW1sTJ28AcXGKOqdRKwCcmQ==", "funding": [ { "type": "opencollective", @@ -6545,14 +6393,12 @@ "type": "github", "url": "https://github.com/sponsors/csstools" } - ], - "license": "MIT-0" + ] }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -6564,7 +6410,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", - "license": "MIT", "dependencies": { "cssnano-preset-default": "^6.1.2", "lilconfig": "^3.1.1" @@ -6584,7 +6429,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", - "license": "MIT", "dependencies": { "autoprefixer": "^10.4.19", "browserslist": "^4.23.0", @@ -6605,7 +6449,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "css-declaration-sorter": "^7.2.0", @@ -6649,7 +6492,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", - "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -6766,7 +6608,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6796,7 +6637,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -6822,7 +6662,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -7593,7 +7432,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "license": "MIT", "dependencies": { "xml-js": "^1.6.11" }, @@ -7605,7 +7443,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -7620,7 +7457,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -7737,7 +7573,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "license": "MIT", "dependencies": { "common-path-prefix": "^3.0.0", "pkg-dir": "^7.0.0" @@ -7753,7 +7588,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "license": "MIT", "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" @@ -7823,7 +7657,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "license": "MIT", "engines": { "node": "*" }, @@ -8163,7 +7996,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -8502,7 +8334,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "license": "MIT", "dependencies": { "camel-case": "^4.1.2", "clean-css": "~5.3.2", @@ -8523,7 +8354,6 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "license": "MIT", "engines": { "node": ">=14" } @@ -8623,7 +8453,6 @@ "url": "https://github.com/sponsors/fb55" } ], - "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -8753,7 +8582,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -8827,7 +8655,6 @@ "version": "0.2.0-alpha.45", "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz", "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==", - "license": "MIT", "engines": { "node": ">=12" } @@ -9165,7 +8992,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -9182,7 +9008,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "license": "MIT", "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -9197,7 +9022,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9357,7 +9181,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", "engines": { "node": ">=14" }, @@ -9395,7 +9218,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "license": "MIT", "dependencies": { "p-locate": "^6.0.0" }, @@ -9419,14 +9241,12 @@ "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "license": "MIT" + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "license": "MIT" + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, "node_modules/longest-streak": { "version": "3.1.0", @@ -11727,7 +11547,6 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", - "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -11797,9 +11616,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -11879,7 +11698,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11911,8 +11729,7 @@ "node_modules/nprogress": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", - "license": "MIT" + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" }, "node_modules/nth-check": { "version": "2.1.1", @@ -11929,7 +11746,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", - "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "schema-utils": "^3.0.0" @@ -11949,7 +11765,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -11965,7 +11780,6 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -11973,14 +11787,12 @@ "node_modules/null-loader/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/null-loader/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -12018,7 +11830,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -12027,7 +11838,6 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -12141,7 +11951,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "license": "MIT", "dependencies": { "yocto-queue": "^1.0.0" }, @@ -12156,7 +11965,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "license": "MIT", "dependencies": { "p-limit": "^4.0.0" }, @@ -12306,8 +12114,7 @@ "node_modules/parse-numeric-range": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", - "license": "ISC" + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" }, "node_modules/parse5": { "version": "7.1.2", @@ -12324,7 +12131,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "license": "MIT", "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" @@ -12356,7 +12162,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } @@ -12435,7 +12240,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "license": "MIT", "dependencies": { "find-up": "^6.3.0" }, @@ -12447,9 +12251,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -12465,9 +12269,9 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -12487,7 +12291,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -12502,7 +12305,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12515,7 +12317,6 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", - "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" @@ -12531,7 +12332,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12556,7 +12356,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -12585,7 +12384,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" @@ -12611,7 +12409,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" @@ -12627,7 +12424,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", @@ -12645,7 +12441,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" @@ -12671,7 +12466,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "@csstools/cascade-layer-name-parser": "^2.0.5", "@csstools/css-parser-algorithms": "^3.0.5", @@ -12686,9 +12480,9 @@ } }, "node_modules/postcss-custom-properties": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.5.tgz", - "integrity": "sha512-UWf/vhMapZatv+zOuqlfLmYXeOhhHLh8U8HAKGI2VJ00xLRYoAJh4xv8iX6FB6+TLXeDnm0DBLMi00E0hodbQw==", + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", + "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", "funding": [ { "type": "github", @@ -12699,7 +12493,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "@csstools/cascade-layer-name-parser": "^2.0.5", "@csstools/css-parser-algorithms": "^3.0.5", @@ -12728,7 +12521,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "@csstools/cascade-layer-name-parser": "^2.0.5", "@csstools/css-parser-algorithms": "^3.0.5", @@ -12746,7 +12538,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12769,7 +12560,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -12784,7 +12574,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12797,7 +12586,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", - "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12809,7 +12597,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", - "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12821,7 +12608,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", - "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12833,7 +12619,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", - "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12845,7 +12630,6 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", - "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.16" }, @@ -12870,7 +12654,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", @@ -12897,7 +12680,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -12912,7 +12694,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12935,7 +12716,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -12950,7 +12730,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12963,7 +12742,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "license": "MIT", "peerDependencies": { "postcss": "^8.1.0" } @@ -12982,7 +12760,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -13004,7 +12781,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" @@ -13030,7 +12806,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "@csstools/css-color-parser": "^3.0.10", "@csstools/css-parser-algorithms": "^3.0.5", @@ -13049,7 +12824,6 @@ "version": "7.3.4", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==", - "license": "MIT", "dependencies": { "cosmiconfig": "^8.3.5", "jiti": "^1.20.0", @@ -13081,7 +12855,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13096,7 +12869,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", - "license": "MIT", "dependencies": { "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" @@ -13112,7 +12884,6 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", "stylehacks": "^6.1.1" @@ -13128,7 +12899,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", @@ -13146,7 +12916,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13161,7 +12930,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", - "license": "MIT", "dependencies": { "colord": "^2.9.3", "cssnano-utils": "^4.0.2", @@ -13178,7 +12946,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "cssnano-utils": "^4.0.2", @@ -13195,7 +12962,6 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", - "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.16" }, @@ -13210,7 +12976,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -13222,7 +12987,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", - "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^7.0.0", @@ -13239,7 +13003,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13252,7 +13015,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", - "license": "ISC", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -13267,7 +13029,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13280,7 +13041,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "license": "ISC", "dependencies": { "icss-utils": "^5.0.0" }, @@ -13292,9 +13052,9 @@ } }, "node_modules/postcss-nesting": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", - "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", + "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", "funding": [ { "type": "github", @@ -13305,9 +13065,8 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { - "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-resolve-nested": "^3.1.0", "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0" }, @@ -13319,9 +13078,9 @@ } }, "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz", - "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", + "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", "funding": [ { "type": "github", @@ -13332,7 +13091,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -13354,7 +13112,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "engines": { "node": ">=18" }, @@ -13366,7 +13123,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13379,7 +13135,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", - "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -13391,7 +13146,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13406,7 +13160,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13421,7 +13174,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13436,7 +13188,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13451,7 +13202,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13466,7 +13216,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" @@ -13482,7 +13231,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13497,7 +13245,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13522,7 +13269,6 @@ "url": "https://liberapay.com/mrcgrtz" } ], - "license": "MIT", "engines": { "node": ">=18" }, @@ -13534,7 +13280,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", - "license": "MIT", "dependencies": { "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" @@ -13560,7 +13305,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13575,7 +13319,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "license": "MIT", "peerDependencies": { "postcss": "^8" } @@ -13594,7 +13337,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13606,9 +13348,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.0.tgz", - "integrity": "sha512-cl13sPBbSqo1Q7Ryb19oT5NZO5IHFolRbIMdgDq4f9w1MHYiL6uZS7uSsjXJ1KzRIcX5BMjEeyxmAevVXENa3Q==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.4.tgz", + "integrity": "sha512-q+lXgqmTMdB0Ty+EQ31SuodhdfZetUlwCA/F0zRcd/XdxjzI+Rl2JhZNz5US2n/7t9ePsvuhCnEN4Bmu86zXlA==", "funding": [ { "type": "github", @@ -13619,9 +13361,8 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { - "@csstools/postcss-cascade-layers": "^5.0.1", + "@csstools/postcss-cascade-layers": "^5.0.2", "@csstools/postcss-color-function": "^4.0.10", "@csstools/postcss-color-mix-function": "^3.0.10", "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.0", @@ -13633,7 +13374,7 @@ "@csstools/postcss-hwb-function": "^4.0.10", "@csstools/postcss-ic-unit": "^4.0.2", "@csstools/postcss-initial": "^2.0.1", - "@csstools/postcss-is-pseudo-class": "^5.0.1", + "@csstools/postcss-is-pseudo-class": "^5.0.3", "@csstools/postcss-light-dark-function": "^2.0.9", "@csstools/postcss-logical-float-and-clear": "^3.0.0", "@csstools/postcss-logical-overflow": "^2.0.0", @@ -13655,7 +13396,7 @@ "@csstools/postcss-trigonometric-functions": "^4.0.9", "@csstools/postcss-unset-value": "^4.0.0", "autoprefixer": "^10.4.21", - "browserslist": "^4.24.5", + "browserslist": "^4.25.0", "css-blank-pseudo": "^7.0.1", "css-has-pseudo": "^7.0.2", "css-prefers-color-scheme": "^10.0.0", @@ -13666,7 +13407,7 @@ "postcss-color-hex-alpha": "^10.0.0", "postcss-color-rebeccapurple": "^10.0.0", "postcss-custom-media": "^11.0.6", - "postcss-custom-properties": "^14.0.5", + "postcss-custom-properties": "^14.0.6", "postcss-custom-selectors": "^8.0.5", "postcss-dir-pseudo-class": "^9.0.1", "postcss-double-position-gradients": "^6.0.2", @@ -13677,7 +13418,7 @@ "postcss-image-set-function": "^7.0.0", "postcss-lab-function": "^7.0.10", "postcss-logical": "^8.1.0", - "postcss-nesting": "^13.0.1", + "postcss-nesting": "^13.0.2", "postcss-opacity-percentage": "^3.0.0", "postcss-overflow-shorthand": "^6.0.0", "postcss-page-break": "^3.0.4", @@ -13707,7 +13448,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -13722,7 +13462,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13735,7 +13474,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13750,7 +13488,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "caniuse-api": "^3.0.0" @@ -13766,7 +13503,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -13781,7 +13517,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "license": "MIT", "peerDependencies": { "postcss": "^8.0.3" } @@ -13800,7 +13535,6 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT", "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -13815,7 +13549,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13828,7 +13561,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13841,7 +13573,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", - "license": "MIT", "dependencies": { "sort-css-media-queries": "2.2.0" }, @@ -13856,7 +13587,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", "svgo": "^3.2.0" @@ -13872,7 +13602,6 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", - "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.16" }, @@ -13886,14 +13615,12 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postcss-zindex": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", - "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -13915,7 +13642,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", - "license": "MIT", "engines": { "node": ">=4" } @@ -13936,7 +13662,6 @@ "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", "engines": { "node": ">=6" } @@ -14186,7 +13911,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.4.1.tgz", "integrity": "sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -14638,7 +14362,6 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "license": "MIT", "engines": { "node": ">=0.10" } @@ -14754,7 +14477,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", - "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0", @@ -14818,8 +14540,7 @@ "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/scheduler": { "version": "0.26.0", @@ -14830,8 +14551,7 @@ "node_modules/schema-dts": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz", - "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==", - "license": "Apache-2.0" + "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==" }, "node_modules/schema-utils": { "version": "4.3.0", @@ -14856,7 +14576,6 @@ "version": "2.17.3", "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", - "license": "MIT", "peer": true }, "node_modules/section-matter": { @@ -15106,7 +14825,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -15276,7 +14994,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", - "license": "MIT", "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", @@ -15294,8 +15011,7 @@ "node_modules/sitemap/node_modules/@types/node": { "version": "17.0.45", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "license": "MIT" + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" }, "node_modules/skin-tone": { "version": "2.0.0", @@ -15321,7 +15037,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -15342,7 +15057,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", - "license": "MIT", "engines": { "node": ">= 6.3.0" } @@ -15356,9 +15070,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -15428,7 +15142,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -15448,8 +15161,7 @@ "node_modules/std-env": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "license": "MIT" + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==" }, "node_modules/string_decoder": { "version": "1.3.0", @@ -15564,7 +15276,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", "engines": { "node": ">=8" }, @@ -15584,7 +15295,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", - "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "postcss-selector-parser": "^6.0.16" @@ -15621,8 +15331,7 @@ "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "license": "MIT" + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" }, "node_modules/svgo": { "version": "3.3.2", @@ -16762,7 +16471,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", - "license": "MIT", "dependencies": { "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -16783,14 +16491,12 @@ "node_modules/webpackbar/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/webpackbar/node_modules/markdown-table": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "license": "MIT", "dependencies": { "repeat-string": "^1.0.0" }, @@ -16803,7 +16509,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -16817,7 +16522,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -16998,7 +16702,6 @@ "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "license": "MIT", "dependencies": { "sax": "^1.2.4" }, @@ -17015,7 +16718,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "license": "MIT", "engines": { "node": ">=12.20" }, diff --git a/docs/package.json b/docs/package.json index 2bb440711..def8214d8 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,9 +14,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/plugin-client-redirects": "^3.8.0", - "@docusaurus/preset-classic": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/plugin-client-redirects": "^3.8.1", + "@docusaurus/preset-classic": "3.8.1", "@mdx-js/react": "^3.1.0", "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", @@ -25,8 +25,8 @@ "react-dom": "^19.1.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.8.0", - "@docusaurus/types": "3.8.0" + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/types": "3.8.1" }, "browserslist": { "production": [ From 50b424e8e644606646e1800e28d68301b7c1b703 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 00:26:36 +0000 Subject: [PATCH 120/282] chore(deps): bump flake8 from 7.2.0 to 7.3.0 (#1326) --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 0e6090497..41c427cf7 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ mypy==1.16.0 -flake8==7.2.0 +flake8==7.3.0 black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version From c75897e7b18c44e53455258836f0408fcfe9c1ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 00:30:46 +0000 Subject: [PATCH 121/282] chore(deps): bump mypy from 1.16.0 to 1.16.1 (#1327) --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 41c427cf7..0603319d7 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.16.0 +mypy==1.16.1 flake8==7.3.0 black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version From 347cb5e0e4f32a26fa8d19268557bf6cd2a42052 Mon Sep 17 00:00:00 2001 From: ewanek1 Date: Wed, 2 Jul 2025 09:53:15 -0700 Subject: [PATCH 122/282] Remove py36 references (#1330) --- .github/maintainers_guide.md | 1 - README.md | 2 +- pyproject.toml | 3 +-- scripts/install_all_and_run_tests.sh | 7 +------ slack_bolt/async_app.py | 2 +- 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index 85b4e13be..4ab491789 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -25,7 +25,6 @@ $ pyenv local 3.8.5 $ pyenv versions system - 3.6.10 3.7.7 * 3.8.5 (set by /path-to-bolt-python/.python-version) diff --git a/README.md b/README.md index 7576597d5..862c63e96 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A Python framework to build Slack apps in a flash with the latest platform featu ## Setup ```bash -# Python 3.6+ required +# Python 3.7+ required python -m venv .venv source .venv/bin/activate diff --git a/pyproject.toml b/pyproject.toml index 5ce2c62bc..5337b5c55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,6 @@ dynamic = ["version", "readme", "dependencies", "authors"] description = "The Bolt Framework for Python" license = { text = "MIT" } classifiers = [ - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -20,7 +19,7 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] -requires-python = ">=3.6" +requires-python = ">=3.7" [project.urls] diff --git a/scripts/install_all_and_run_tests.sh b/scripts/install_all_and_run_tests.sh index 21660dca5..e71c8511b 100755 --- a/scripts/install_all_and_run_tests.sh +++ b/scripts/install_all_and_run_tests.sh @@ -16,12 +16,7 @@ pip uninstall python-lambda test_target="$1" python_version=`python --version | awk '{print $2}'` -if [ ${python_version:0:3} == "3.6" ] -then - pip install -U -r requirements.txt -else - pip install -e . -fi +pip install -e . if [[ $test_target != "" ]] then diff --git a/slack_bolt/async_app.py b/slack_bolt/async_app.py index 10878c51b..fdf724d4c 100644 --- a/slack_bolt/async_app.py +++ b/slack_bolt/async_app.py @@ -5,7 +5,7 @@ If you'd prefer to build your app with [asyncio](https://docs.python.org/3/library/asyncio.html), you can import the [AIOHTTP](https://docs.aiohttp.org/en/stable/) library and call the `AsyncApp` constructor. Within async apps, you can use the async/await pattern. ```bash -# Python 3.6+ required +# Python 3.7+ required python -m venv .venv source .venv/bin/activate From 46e25d3e1f02a9a50bc1b214f3772957473d70a5 Mon Sep 17 00:00:00 2001 From: ewanek1 Date: Wed, 2 Jul 2025 12:16:29 -0700 Subject: [PATCH 123/282] Remove py36 references (#1331) --- .github/maintainers_guide.md | 1 + README.md | 2 +- pyproject.toml | 3 ++- scripts/install_all_and_run_tests.sh | 7 ++++++- slack_bolt/async_app.py | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index 4ab491789..85b4e13be 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -25,6 +25,7 @@ $ pyenv local 3.8.5 $ pyenv versions system + 3.6.10 3.7.7 * 3.8.5 (set by /path-to-bolt-python/.python-version) diff --git a/README.md b/README.md index 862c63e96..7576597d5 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A Python framework to build Slack apps in a flash with the latest platform featu ## Setup ```bash -# Python 3.7+ required +# Python 3.6+ required python -m venv .venv source .venv/bin/activate diff --git a/pyproject.toml b/pyproject.toml index 5337b5c55..5ce2c62bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dynamic = ["version", "readme", "dependencies", "authors"] description = "The Bolt Framework for Python" license = { text = "MIT" } classifiers = [ + "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -19,7 +20,7 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] -requires-python = ">=3.7" +requires-python = ">=3.6" [project.urls] diff --git a/scripts/install_all_and_run_tests.sh b/scripts/install_all_and_run_tests.sh index e71c8511b..21660dca5 100755 --- a/scripts/install_all_and_run_tests.sh +++ b/scripts/install_all_and_run_tests.sh @@ -16,7 +16,12 @@ pip uninstall python-lambda test_target="$1" python_version=`python --version | awk '{print $2}'` -pip install -e . +if [ ${python_version:0:3} == "3.6" ] +then + pip install -U -r requirements.txt +else + pip install -e . +fi if [[ $test_target != "" ]] then diff --git a/slack_bolt/async_app.py b/slack_bolt/async_app.py index fdf724d4c..10878c51b 100644 --- a/slack_bolt/async_app.py +++ b/slack_bolt/async_app.py @@ -5,7 +5,7 @@ If you'd prefer to build your app with [asyncio](https://docs.python.org/3/library/asyncio.html), you can import the [AIOHTTP](https://docs.aiohttp.org/en/stable/) library and call the `AsyncApp` constructor. Within async apps, you can use the async/await pattern. ```bash -# Python 3.7+ required +# Python 3.6+ required python -m venv .venv source .venv/bin/activate From 1e69f1d0c9a68c9967864f52e50544c19b2b1d3b Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:05:33 -0700 Subject: [PATCH 124/282] Docs: Updates Modals tutorial to not use Glitch (#1334) --- docs/content/tutorial/modals.md | 166 +++++++++++++++++++------------- 1 file changed, 100 insertions(+), 66 deletions(-) diff --git a/docs/content/tutorial/modals.md b/docs/content/tutorial/modals.md index 678d3783b..b6d672d08 100644 --- a/docs/content/tutorial/modals.md +++ b/docs/content/tutorial/modals.md @@ -1,101 +1,135 @@ + # Modals -If you're learning about Slack apps, modals, or slash commands for the first time, you've come to the right place! In this tutorial, we'll take a look at setting up your very own server using Glitch, and using that server to run your Slack app. +If you're learning about Slack apps, modals, or slash commands for the first time, you've come to the right place! In this tutorial, we'll take a look at setting up your very own server using GitHub Codespaces, then using that server to run your Slack app built with the [**Bolt for Python framework**](https://github.com/SlackAPI/bolt-python). -Let's take a look at the technologies we'll use in this tutorial: +:::info[GitHub Codespaces] +GitHub Codespaces is an online IDE that allows you to work on code and host your own server at the same time. While Codespaces is good for testing and development purposes, it should not be used in production. -* Glitch is a online IDE that allows you to collaboratively work on code and host your own server. Glitch should only be used for development purposes and should not be used in production. -* We'll use Python in conjunction with our [Bolt for Python](https://github.com/SlackAPI/bolt-python) SDK. -* [Block Kit](https://docs.slack.dev/block-kit/) is a UI framework for Slack apps that allows you to create beautiful, interactive messages within Slack. If you've ever seen a message in Slack with buttons or a select menu, that's Block Kit. -* Modals are similar to a pop-up window that displays right in Slack. They grab the attention of the user, and are normally used to prompt users to provide some kind of information or input. +::: ---- +At the end of this tutorial, your final app will look like this: -## Final product overview {#final_product} -If you follow through with the extra credit tasks, your final app will look like this: +![announce](https://github.com/user-attachments/assets/0bf1c2f0-4b22-4c9c-98b3-b21e9bcc14a8) -![Final product](/img/tutorials/modals/final_product.gif) +And will make use of these Slack concepts: +* [**Block Kit**](https://docs.slack.dev/block-kit/) is a UI framework for Slack apps that allows you to create beautiful, interactive messages within Slack. If you've ever seen a message in Slack with buttons or a select menu, that's Block Kit. +* [**Modals**](https://docs.slack.dev/surfaces/modals) are a pop-up window that displays right in Slack. They grab the attention of the user, and are normally used to prompt users to provide some kind of information or input in a form. +* [**Slash Commands**](https://docs.slack.dev/interactivity/implementing-slash-commands) allow you to invoke your app within Slack by just typing into the message composer box. e.g. `/remind`, `/topic`. ---- +If you're familiar with using Heroku you can also deploy directly to Heroku with the following button. -## The process {#steps} +[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://www.heroku.com/deploy?template=https://github.com/wongjas/modal-example) -1. [Create a new app](https://api.slack.com/apps/new) and name it whatever you like. +--- + +## Setting up your app within App Settings {#setting-up-app-settings} -2. [Remix (or clone)](https://glitch.com/edit/#!/remix/intro-to-modals-bolt) the Glitch template. +You'll need to create an app and configure it properly within App Settings before using it. -Here's a copy of what the modal payload looks like — this is what powers the modal. +1. [Create a new app](https://api.slack.com/apps/new), click `From a Manifest`, and choose the workspace that you want to develop on. Then copy the following JSON object; it describes the metadata about your app, like its name, its bot display name and permissions it will request. ```json { - "type": "modal", - "callback_id": "gratitude-modal", - "title": { - "type": "plain_text", - "text": "Gratitude Box", - "emoji": true - }, - "submit": { - "type": "plain_text", - "text": "Submit", - "emoji": true - }, - "close": { - "type": "plain_text", - "text": "Cancel", - "emoji": true - }, - "blocks": [ - { - "type": "input", - "block_id": "my_block", - "element": { - "type": "plain_text_input", - "action_id": "my_action" - }, - "label": { - "type": "plain_text", - "text": "Say something nice!", - "emoji": true - } + "display_information": { + "name": "Intro to Modals" + }, + "features": { + "bot_user": { + "display_name": "Intro to Modals", + "always_online": false + }, + "slash_commands": [ + { + "command": "/announce", + "description": "Makes an announcement", + "should_escape": false + } + ] + }, + "oauth_config": { + "scopes": { + "bot": [ + "chat:write", + "commands" + ] + } + }, + "settings": { + "interactivity": { + "is_enabled": true + }, + "org_deploy_enabled": false, + "socket_mode_enabled": true, + "token_rotation_enabled": false } - ] } ``` -3. Find the base path to your server by clicking **Share**, then copy the Live site link. +2. Once your app has been created, scroll down to `App-Level Tokens` and create a token that requests for the [`connections:write`](https://docs.slack.dev/reference/scopes/connections.write) scope, which allows you to use [Socket Mode](https://docs.slack.dev/apis/events-api/using-socket-mode), a secure way to develop on Slack through the use of WebSockets. Copy the value of your app token and keep it for safe-keeping. + +3. Install your app by heading to `Install App` in the left sidebar. Hit `Allow`, which means you're agreeing to install your app with the permissions that it is requesting. Be sure to copy the token that you receive, and keep it somewhere secret and safe. - ![Get the base link](/img/tutorials/modals/base_link.gif) +## Starting your Codespaces server {#starting-server} -4. On your app page, navigate to **Interactivity & Shortcuts**. Append "/slack/events" to your base path URL and enter it into the **Request URL** e.g., `https://festive-harmonious-march.glitch.me/slack/events`. This allows your server to retrieve information from the modal. You can see the code for this within the Glitch project. +1. Log into GitHub and head to this [repository](https://github.com/wongjas/modal-example). - ![Interactivity URL](/img/tutorials/modals/interactivity_url.png) +2. Click the green `Code` button and hit the `Codespaces` tab and then `Create codespace on main`. This will bring up a code editor within your browser so you can start coding. -5. Create the slash command so you can access it within Slack. Navigate to the **Slash Commands** section and create a new command. Note the **Request URL** is the same link as above, e.g. `https://festive-harmonious-march.glitch.me/slack/events` . The code that powers the slash command and opens a modal can be found within the Glitch project. +## Understanding the project files {#understanding-files} - ![Slash command details](/img/tutorials/modals/slash_command.png) +Within the project you'll find a `manifest.json` file. This is a a configuration file used by Slack apps. With a manifest, you can create an app with a pre-defined configuration, or adjust the configuration of an existing app. -6. Select **Install App**. After you've done this, you'll see a **Bot User OAuth Access Token**, copy this. +The `simple_modal_example.py` Python script contains the code that powers your app. If you're going to tinker with the app itself, take a look at the comments found within the `simple_modal_example.py` file! -7. Navigate to your Glitch project and click the `.env` file where the credentials are stored, and paste your bot token where the `SLACK_BOT_TOKEN` variable is shown. This allows your server to send authenticated requests to the Slack API. You'll also need to head to your app's settings page under **Basic Information** and copy the _Signing secret_ to place into the `SLACK_SIGNING_SECRET` variable. +The `requirements.txt` file contains the Python package dependencies needed to run this app. - ![Environment variables](/img/tutorials/modals/heart_icon.gif) +:::info[This repo contains optional Heroku-specific configurations] -8. Test by heading to Slack and typing `/thankyou`. +The `app.json` file defines your Heroku app configuration including environment variables and deployment settings, to allow your app to deploy with one click. `Procfile` is a Heroku-specific file that tells Heroku what command to run when starting your app — in this case a Python script would run as a `worker` process. If you aren't deploying to Heroku, you can ignore both these files. -All done! 🎉 You've created your first slash command using Block Kit and modals! The world is your oyster; you can create more complex modals by playing around with [Block Kit Builder](https://app.slack.com/block-kit-builder). +::: + +## Adding tokens {#adding-tokens} + +1. Open a terminal up within the browser's editor. + +2. Grab the app and bot tokens that you kept safe. We're going to set them as environment variables. + +```bash +export SLACK_APP_TOKEN= +export SLACK_BOT_TOKEN= +``` -### Extra credit {#extra_credit} +## Running the app {#running-app} + +1. Activate a virtual environment for your Python packages to be installed. + +```bash +# Setup your python virtual environment +python3 -m venv .venv +source .venv/bin/activate +``` + +2. Install the dependencies from the `requirements.txt` file. + + +```bash +# Install the dependencies +pip install -r requirements.txt +``` + +3. Start your app using the `python3 simple_modal_example.py` command. + +```bash +# Start your local server +python3 simple_modal_example.py +``` -For a little extra credit, let's post the feedback we received in a channel. +4. Now that your app is running, you should be able to see it within Slack. Test this by heading to Slack and typing `/announce`. -1. Add the `chat:write` bot scope, which allows your bot to post messages within Slack. You can do this in the **OAuth & Permissions** section for your Slack app. -2. Reinstall your app to apply the scope. -3. Create a channel and name it `#thanks`. Get its ID by right clicking the channel name, copying the link, and copying the last part starting with the letter `C`. For example, if your channel link looks like this: https://my.slack.com/archives/C123FCN2MLM, the ID is `C123FCN2MLM`. -4. Add your bot to the channel by typing the command `/invite @your_bots_name`. -5. Uncomment the `Extra Credit` code within your Glitch project and make sure to replace `your_channel_id` with the ID above. -6. Test it out by typing `/thankyou`, and watching all the feedback come into your channel! +All done! 🎉 You've created your first slash command using Block Kit and modals! The world is your oyster; play around with [Block Kit Builder](https://app.slack.com/block-kit-builder) and create more complex modals and place them in your code to see what happens! ## Next steps {#next-steps} -If you want to learn more about Bolt for Python, refer to the [Getting Started guide](/bolt-python/getting-started). +If you want to learn more about Bolt for Python, refer to the [Getting Started guide](https://tools.slack.dev/bolt-python/getting-started). \ No newline at end of file From e6b34ebba6c5f43d72e006190e9072a7af5b3ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:05:45 -0400 Subject: [PATCH 125/282] chore(deps): bump on-headers and compression in /docs (#1336) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 6790558aa..773665646 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5828,16 +5828,16 @@ } }, "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -11872,9 +11872,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" From 88ddc7cbe1cf23b45dd4349a357f60d9bfcaa1a6 Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:43:07 -0500 Subject: [PATCH 126/282] Docs: Update language around AI Apps (#1335) --- docs/content/concepts/ai-apps.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/content/concepts/ai-apps.md b/docs/content/concepts/ai-apps.md index d30513bdd..a8da2bf1f 100644 --- a/docs/content/concepts/ai-apps.md +++ b/docs/content/concepts/ai-apps.md @@ -1,5 +1,5 @@ --- -title: AI Apps +title: Using AI in Apps lang: en slug: /concepts/ai-apps --- @@ -8,7 +8,7 @@ slug: /concepts/ai-apps If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. ::: -AI apps comprise a new messaging experience for Slack. If you're unfamiliar with using AI apps within Slack, you'll want to read the [API documentation on the subject](https://docs.slack.dev/ai/). Then come back here to implement them with Bolt! +The Agents & AI Apps feature comprises a unique messaging experience for Slack. If you're unfamiliar with using the Agents & AI Apps feature within Slack, you'll want to read the [API documentation on the subject](https://docs.slack.dev/ai/). Then come back here to implement them with Bolt! ## Configuring your app to support AI features {#configuring-your-app} @@ -25,12 +25,12 @@ AI apps comprise a new messaging experience for Slack. If you're unfamiliar with * [`message.im`](https://docs.slack.dev/reference/events/message.im) :::info -You _could_ implement your own AI app by [listening](event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events (see implementation details below). That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! +You _could_ go it alone and [listen](event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events (see implementation details below) in order to implement the AI features in your app. That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! ::: ## The `Assistant` class instance {#assistant-class} -The `Assistant` class can be used to handle the incoming events expected from a user interacting with an AI app in Slack. A typical flow would look like: +The `Assistant` class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. A typical flow would look like: 1. [The user starts a thread](#handling-a-new-thread). The `Assistant` class handles the incoming [`assistant_thread_started`](https://docs.slack.dev/reference/events/assistant_thread_started) event. 2. [The thread context may change at any point](#handling-thread-context-changes). The `Assistant` class can handle any incoming [`assistant_thread_context_changed`](https://docs.slack.dev/reference/events/assistant_thread_context_changed) events. The class also provides a default context store to keep track of thread context changes as the user moves through Slack. @@ -107,13 +107,13 @@ Refer to the [module document](https://tools.slack.dev/bolt-python/api-docs/slac ## Handling a new thread {#handling-a-new-thread} -When the user opens a new thread with your AI app, the [`assistant_thread_started`](https://docs.slack.dev/reference/events/assistant_thread_started) event will be sent to your app. +When the user opens a new thread with your AI-enabled app, the [`assistant_thread_started`](https://docs.slack.dev/reference/events/assistant_thread_started) event will be sent to your app. :::tip -When a user opens an AI app thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data. You can grab that info by using the `get_thread_context` utility, as subsequent user message event payloads won't include the channel info. +When a user opens an app thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data. You can grab that info by using the `get_thread_context` utility, as subsequent user message event payloads won't include the channel info. ::: -### Block Kit interactions in the AI app thread {#block-kit-interactions} +### Block Kit interactions in the app thread {#block-kit-interactions} For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](https://docs.slack.dev/messaging/message-metadata/) to trigger subsequent interactions with the user. From beba392234ca149a8f3b2cd65141619a09aeea3d Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 24 Jul 2025 19:49:22 -0700 Subject: [PATCH 127/282] docs: filter against bot_id in listener middleware example (#1339) --- docs/content/concepts/listener-middleware.md | 11 +++++------ .../current/concepts/listener-middleware.md | 13 ++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/content/concepts/listener-middleware.md b/docs/content/concepts/listener-middleware.md index 338cb0d4f..3507d7d97 100644 --- a/docs/content/concepts/listener-middleware.md +++ b/docs/content/concepts/listener-middleware.md @@ -11,11 +11,10 @@ If your listener middleware is a quite simple one, you can use a listener matche 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. ```python -# Listener middleware which filters out messages with "bot_message" subtype +# Listener middleware which filters out messages from a bot def no_bot_messages(message, next): - subtype = message.get("subtype") - if subtype != "bot_message": - next() + if "bot_id" not in message: + next() # This listener only receives messages from humans @app.event(event="message", middleware=[no_bot_messages]) @@ -24,10 +23,10 @@ def log_message(logger, event): # Listener matchers: simplified version of listener middleware def no_bot_messages(message) -> bool: - return message.get("subtype") != "bot_message" + return "bot_id" not in message @app.event( - event="message", + event="message", matchers=[no_bot_messages] # or matchers=[lambda message: message.get("subtype") != "bot_message"] ) diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/listener-middleware.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/listener-middleware.md index 822b5ac63..a013dde42 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/listener-middleware.md +++ b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/listener-middleware.md @@ -11,11 +11,10 @@ slug: /concepts/listener-middleware 指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python -# "bot_message" サブタイプのメッセージを抽出するリスナーミドルウェア +# ボットからのメッセージをフィルタリングするリスナーミドルウェア def no_bot_messages(message, next): - subtype = message.get("subtype") - if subtype != "bot_message": - next() + if "bot_id" not in message: + next() # このリスナーは人間によって送信されたメッセージのみを受け取ります @app.event(event="message", middleware=[no_bot_messages]) @@ -24,13 +23,13 @@ def log_message(logger, event): # リスナーマッチャー: 簡略化されたバージョンのリスナーミドルウェア def no_bot_messages(message) -> bool: - return message.get("subtype") != "bot_message" + return "bot_id" not in message @app.event( - event="message", + event="message", matchers=[no_bot_messages] # or matchers=[lambda message: message.get("subtype") != "bot_message"] ) def log_message(logger, event): logger.info(f"(MSG) User: {event['user']}\nMessage: {event['text']}") -``` \ No newline at end of file +``` From df3c426093dd3349ba05dae49820e3709117b25b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:53:42 -0400 Subject: [PATCH 128/282] chore(deps): update pytest requirement from <8.4,>=6.2.5 to >=6.2.5,<8.5 (#1328) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/testing_without_asyncio.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/testing_without_asyncio.txt b/requirements/testing_without_asyncio.txt index 754889faf..d10c4345e 100644 --- a/requirements/testing_without_asyncio.txt +++ b/requirements/testing_without_asyncio.txt @@ -1,3 +1,3 @@ # pip install -r requirements/testing_without_asyncio.txt -pytest>=6.2.5,<8.4 # https://github.com/tornadoweb/tornado/issues/3375 +pytest>=6.2.5,<8.5 # https://github.com/tornadoweb/tornado/issues/3375 pytest-cov>=3,<7 From 1b876abb16b1cd9f0a6bf8ab8decaaa7a179e85c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:42:14 -0700 Subject: [PATCH 129/282] chore(deps): bump mypy from 1.16.1 to 1.17.1 (#1343) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 0603319d7..c3a383a13 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.16.1 +mypy==1.17.1 flake8==7.3.0 black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version From dd4d622e8a407567dfbd22b0b36a741b51c574fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 22:47:10 +0000 Subject: [PATCH 130/282] chore(deps): bump the react group in /docs with 2 updates (#1344) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package-lock.json | 18 +++++++++--------- docs/package.json | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 773665646..ec7f3558a 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -15,8 +15,8 @@ "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", "prism-react-renderer": "^2.4.1", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "react": "^19.1.1", + "react-dom": "^19.1.1" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.8.1", @@ -13858,24 +13858,24 @@ } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "license": "MIT", "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.1.1" } }, "node_modules/react-fast-compare": { diff --git a/docs/package.json b/docs/package.json index def8214d8..b67d8a7a4 100644 --- a/docs/package.json +++ b/docs/package.json @@ -21,8 +21,8 @@ "clsx": "^2.0.0", "docusaurus-theme-github-codeblock": "^2.0.2", "prism-react-renderer": "^2.4.1", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "react": "^19.1.1", + "react-dom": "^19.1.1" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.8.1", From 9596797e55f56c92a07868595f002eb2751f0739 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 23:45:47 +0000 Subject: [PATCH 131/282] chore(deps): bump slackapi/slack-github-action from 2.1.0 to 2.1.1 (#1345) --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4fe757b1a..f53a603ff 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -89,7 +89,7 @@ jobs: if: failure() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' steps: - name: Send notifications of failing tests - uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0 + uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1 with: errors: true webhook: ${{ secrets.SLACK_REGRESSION_FAILURES_WEBHOOK_URL }} From eb3a51923a38fda54f89515dd9e3853014ffaf64 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Thu, 7 Aug 2025 12:04:46 -0700 Subject: [PATCH 132/282] Build: remove docusaurus configuration files (#1342) --- .github/workflows/docs-deploy.yml | 65 - README.md | 6 +- docs/.gitignore | 3 - docs/README.md | 129 - docs/babel.config.js | 3 - docs/content/concepts/custom-steps.md | 154 - docs/content/concepts/web-api.md | 28 - docs/docusaurus.config.js | 114 - docs/english/_sidebar.json | 209 + docs/{content => english}/building-an-app.md | 49 +- .../concepts/acknowledge.md | 14 +- docs/{content => english}/concepts/actions.md | 14 +- .../{content => english}/concepts/adapters.md | 6 +- docs/{content => english}/concepts/ai-apps.md | 62 +- .../{content => english}/concepts/app-home.md | 14 +- docs/{content => english}/concepts/async.md | 6 +- .../concepts/authenticating-oauth.md | 12 +- .../concepts/authorization.md | 8 +- .../{content => english}/concepts/commands.md | 10 +- docs/{content => english}/concepts/context.md | 6 +- .../concepts/custom-adapters.md | 8 +- .../concepts/custom-steps-dynamic-options.md | 32 +- .../concepts/custom-steps.md | 16 +- docs/{content => english}/concepts/errors.md | 6 +- .../concepts/event-listening.md | 12 +- .../concepts/global-middleware.md | 8 +- .../concepts/lazy-listeners.md | 6 +- .../concepts/listener-middleware.md | 8 +- docs/{content => english}/concepts/logging.md | 6 +- .../concepts/message-listening.md | 13 +- .../concepts/message-sending.md | 12 +- .../concepts/opening-modals.md | 12 +- .../concepts/select-menu-options.md | 14 +- .../concepts/shortcuts.md | 18 +- .../concepts/socket-mode.md | 10 +- .../concepts/token-rotation.md | 10 +- .../concepts/updating-pushing-views.md | 14 +- .../concepts/view-submissions.md | 16 +- docs/english/concepts/web-api.md | 22 + docs/{content => english}/getting-started.md | 39 +- docs/{content => english}/index.md | 2 +- .../legacy}/steps-from-apps.md | 38 +- .../tutorial}/ai-chatbot/1.png | Bin .../tutorial}/ai-chatbot/2.png | Bin .../tutorial}/ai-chatbot/3.png | Bin .../tutorial}/ai-chatbot/4.png | Bin .../tutorial}/ai-chatbot/5.png | Bin .../tutorial}/ai-chatbot/6.png | Bin .../tutorial}/ai-chatbot/7.png | Bin .../tutorial}/ai-chatbot/8.png | Bin .../tutorial/ai-chatbot}/ai-chatbot.md | 26 +- .../tutorial/custom-steps-for-jira}/1.png | Bin .../tutorial/custom-steps-for-jira}/2.png | Bin .../tutorial/custom-steps-for-jira}/3.png | Bin .../tutorial/custom-steps-for-jira}/4.png | Bin .../tutorial/custom-steps-for-jira}/5.png | Bin .../tutorial/custom-steps-for-jira}/6.png | Bin .../tutorial/custom-steps-for-jira}/7.png | Bin .../custom-steps-for-jira.md | 24 +- .../add-step.png | Bin .../app-message.png | Bin .../custom-steps-workflow-builder-existing.md | 26 +- .../define-step.png | Bin .../find-step.png | Bin .../inputs.png | Bin .../org-ready.png | Bin .../outputs.png | Bin .../step-inputs.png | Bin .../app-token.png | Bin .../bot-token.png | Bin .../custom-steps-workflow-builder-new.md | 49 +- .../install.png | Bin .../manifest.png | Bin .../wfb-1.png | Bin .../wfb-10.png | Bin .../wfb-11.png | Bin .../wfb-12.png | Bin .../wfb-2.png | Bin .../wfb-3.png | Bin .../wfb-4.png | Bin .../wfb-5.png | Bin .../wfb-6.png | Bin .../wfb-7.png | Bin .../wfb-8.png | Bin .../wfb-9.png | Bin .../workflow-step.png | Bin .../tutorial/custom-steps.md | 12 +- .../tutorial}/modals/base_link.gif | Bin .../tutorial}/modals/final_product.gif | Bin .../tutorial}/modals/heart_icon.gif | Bin .../tutorial}/modals/interactivity_url.png | Bin .../tutorial/modals}/modals.md | 11 +- .../tutorial}/modals/slash_command.png | Bin docs/footerConfig.js | 21 - docs/i18n/ja-jp/README.md | 121 - docs/i18n/ja-jp/code.json | 321 - .../current.json | 78 - .../current/concepts/app-home.md | 43 - .../current/concepts/web-api.md | 23 - .../docusaurus-theme-classic/footer.json | 6 - .../docusaurus-theme-classic/navbar.json | 62 - .../boltpy => img}/basic-information-page.png | Bin docs/{static/img/boltpy => img}/bot-token.png | Bin .../concepts/acknowledge.md | 12 +- .../current => japanese}/concepts/actions.md | 14 +- .../current => japanese}/concepts/adapters.md | 6 +- docs/japanese/concepts/app-home.md | 40 + .../concepts/assistant.md | 16 +- .../current => japanese}/concepts/async.md | 6 +- .../concepts/authenticating-oauth.md | 12 +- .../concepts/authorization.md | 8 +- .../current => japanese}/concepts/commands.md | 10 +- .../current => japanese}/concepts/context.md | 6 +- .../concepts/custom-adapters.md | 8 +- .../current => japanese}/concepts/errors.md | 6 +- .../concepts/event-listening.md | 12 +- .../concepts/global-middleware.md | 9 +- .../concepts/lazy-listeners.md | 6 +- .../concepts/listener-middleware.md | 8 +- .../current => japanese}/concepts/logging.md | 6 +- .../concepts/message-listening.md | 10 +- .../concepts/message-sending.md | 12 +- .../concepts/opening-modals.md | 12 +- .../concepts/select-menu-options.md | 14 +- .../concepts/shortcuts.md | 18 +- .../concepts/socket-mode.md | 10 +- .../concepts/token-rotation.md | 10 +- .../concepts/updating-pushing-views.md | 14 +- .../concepts/view-submissions.md | 16 +- docs/japanese/concepts/web-api.md | 19 + .../current => japanese}/getting-started.md | 62 +- .../legacy/steps-from-apps.md | 30 +- docs/navbarConfig.js | 97 - docs/package-lock.json | 16738 ---------------- docs/package.json | 46 - .../adapter/aiohttp/index.html | 0 .../adapter/asgi/aiohttp/index.html | 0 .../adapter/asgi/async_handler.html | 0 .../adapter/asgi/base_handler.html | 0 .../adapter/asgi/builtin/index.html | 0 .../adapter/asgi/http_request.html | 0 .../adapter/asgi/http_response.html | 0 .../adapter/asgi/index.html | 0 .../adapter/asgi/utils.html | 0 .../adapter/aws_lambda/chalice_handler.html | 0 .../chalice_lazy_listener_runner.html | 0 .../adapter/aws_lambda/handler.html | 0 .../adapter/aws_lambda/index.html | 0 .../adapter/aws_lambda/internals.html | 0 .../aws_lambda/lambda_s3_oauth_flow.html | 0 .../aws_lambda/lazy_listener_runner.html | 0 .../aws_lambda/local_lambda_client.html | 0 .../adapter/bottle/handler.html | 0 .../adapter/bottle/index.html | 0 .../adapter/cherrypy/handler.html | 0 .../adapter/cherrypy/index.html | 0 .../adapter/django/handler.html | 0 .../adapter/django/index.html | 0 .../adapter/falcon/async_resource.html | 0 .../adapter/falcon/index.html | 0 .../adapter/falcon/resource.html | 0 .../adapter/fastapi/async_handler.html | 0 .../adapter/fastapi/index.html | 0 .../adapter/flask/handler.html | 0 .../adapter/flask/index.html | 0 .../google_cloud_functions/handler.html | 0 .../adapter/google_cloud_functions/index.html | 0 .../adapter/index.html | 0 .../adapter/pyramid/handler.html | 0 .../adapter/pyramid/index.html | 0 .../adapter/sanic/async_handler.html | 0 .../adapter/sanic/index.html | 0 .../adapter/socket_mode/aiohttp/index.html | 0 .../socket_mode/async_base_handler.html | 0 .../adapter/socket_mode/async_handler.html | 0 .../adapter/socket_mode/async_internals.html | 0 .../adapter/socket_mode/base_handler.html | 0 .../adapter/socket_mode/builtin/index.html | 0 .../adapter/socket_mode/index.html | 0 .../adapter/socket_mode/internals.html | 0 .../socket_mode/websocket_client/index.html | 0 .../adapter/socket_mode/websockets/index.html | 0 .../adapter/starlette/async_handler.html | 0 .../adapter/starlette/handler.html | 0 .../adapter/starlette/index.html | 0 .../adapter/tornado/async_handler.html | 0 .../adapter/tornado/handler.html | 0 .../adapter/tornado/index.html | 0 .../adapter/wsgi/handler.html | 0 .../adapter/wsgi/http_request.html | 0 .../adapter/wsgi/http_response.html | 0 .../adapter/wsgi/index.html | 0 .../adapter/wsgi/internals.html | 0 .../slack_bolt => reference}/app/app.html | 0 .../app/async_app.html | 0 .../app/async_server.html | 0 .../slack_bolt => reference}/app/index.html | 0 .../slack_bolt => reference}/async_app.html | 0 .../authorization/async_authorize.html | 0 .../authorization/async_authorize_args.html | 0 .../authorization/authorize.html | 0 .../authorization/authorize_args.html | 0 .../authorization/authorize_result.html | 0 .../authorization/index.html | 0 .../context/ack/ack.html | 0 .../context/ack/async_ack.html | 0 .../context/ack/index.html | 0 .../context/ack/internals.html | 0 .../assistant/assistant_utilities.html | 0 .../assistant/async_assistant_utilities.html | 0 .../context/assistant/index.html | 0 .../context/assistant/internals.html | 0 .../assistant/thread_context/index.html | 0 .../thread_context_store/async_store.html | 0 .../default_async_store.html | 0 .../thread_context_store/default_store.html | 0 .../thread_context_store/file/index.html | 0 .../assistant/thread_context_store/index.html | 0 .../assistant/thread_context_store/store.html | 0 .../context/async_context.html | 0 .../context/base_context.html | 0 .../context/complete/async_complete.html | 0 .../context/complete/complete.html | 0 .../context/complete/index.html | 0 .../context/context.html | 0 .../context/fail/async_fail.html | 0 .../context/fail/fail.html | 0 .../context/fail/index.html | 0 .../async_get_thread_context.html | 0 .../get_thread_context.html | 0 .../context/get_thread_context/index.html | 0 .../context/index.html | 0 .../context/respond/async_respond.html | 0 .../context/respond/index.html | 0 .../context/respond/internals.html | 0 .../context/respond/respond.html | 0 .../async_save_thread_context.html | 0 .../context/save_thread_context/index.html | 0 .../save_thread_context.html | 0 .../context/say/async_say.html | 0 .../context/say/index.html | 0 .../context/say/internals.html | 0 .../context/say/say.html | 0 .../context/set_status/async_set_status.html | 0 .../context/set_status/index.html | 0 .../context/set_status/set_status.html | 0 .../async_set_suggested_prompts.html | 0 .../context/set_suggested_prompts/index.html | 0 .../set_suggested_prompts.html | 0 .../context/set_title/async_set_title.html | 0 .../context/set_title/index.html | 0 .../context/set_title/set_title.html | 0 .../slack_bolt => reference}/error/index.html | 0 .../slack_bolt => reference}/index.html | 0 .../kwargs_injection/args.html | 0 .../kwargs_injection/async_args.html | 0 .../kwargs_injection/async_utils.html | 0 .../kwargs_injection/index.html | 0 .../kwargs_injection/utils.html | 0 .../lazy_listener/async_internals.html | 0 .../lazy_listener/async_runner.html | 0 .../lazy_listener/asyncio_runner.html | 0 .../lazy_listener/index.html | 0 .../lazy_listener/internals.html | 0 .../lazy_listener/runner.html | 0 .../lazy_listener/thread_runner.html | 0 .../listener/async_builtins.html | 0 .../listener/async_listener.html | 0 .../async_listener_completion_handler.html | 0 .../async_listener_error_handler.html | 0 .../async_listener_start_handler.html | 0 .../listener/asyncio_runner.html | 0 .../listener/builtins.html | 0 .../listener/custom_listener.html | 0 .../listener/index.html | 0 .../listener/listener.html | 0 .../listener/listener_completion_handler.html | 0 .../listener/listener_error_handler.html | 0 .../listener/listener_start_handler.html | 0 .../listener/thread_runner.html | 0 .../listener_matcher/async_builtins.html | 0 .../async_listener_matcher.html | 0 .../listener_matcher/builtins.html | 0 .../custom_listener_matcher.html | 0 .../listener_matcher/index.html | 0 .../listener_matcher/listener_matcher.html | 0 .../logger/index.html | 0 .../logger/messages.html | 0 .../middleware/assistant/assistant.html | 0 .../middleware/assistant/async_assistant.html | 0 .../middleware/assistant/index.html | 0 .../middleware/async_builtins.html | 0 .../middleware/async_custom_middleware.html | 0 .../middleware/async_middleware.html | 0 .../async_middleware_error_handler.html | 0 .../async_attaching_function_token.html | 0 .../attaching_function_token.html | 0 .../attaching_function_token/index.html | 0 .../authorization/async_authorization.html | 0 .../authorization/async_internals.html | 0 .../async_multi_teams_authorization.html | 0 .../async_single_team_authorization.html | 0 .../authorization/authorization.html | 0 .../middleware/authorization/index.html | 0 .../middleware/authorization/internals.html | 0 .../multi_teams_authorization.html | 0 .../single_team_authorization.html | 0 .../middleware/custom_middleware.html | 0 .../async_ignoring_self_events.html | 0 .../ignoring_self_events.html | 0 .../ignoring_self_events/index.html | 0 .../middleware/index.html | 0 .../async_message_listener_matches.html | 0 .../message_listener_matches/index.html | 0 .../message_listener_matches.html | 0 .../middleware/middleware.html | 0 .../middleware/middleware_error_handler.html | 0 .../async_request_verification.html | 0 .../request_verification/index.html | 0 .../request_verification.html | 0 .../middleware/ssl_check/async_ssl_check.html | 0 .../middleware/ssl_check/index.html | 0 .../middleware/ssl_check/ssl_check.html | 0 .../async_url_verification.html | 0 .../middleware/url_verification/index.html | 0 .../url_verification/url_verification.html | 0 .../oauth/async_callback_options.html | 0 .../oauth/async_internals.html | 0 .../oauth/async_oauth_flow.html | 0 .../oauth/async_oauth_settings.html | 0 .../oauth/callback_options.html | 0 .../slack_bolt => reference}/oauth/index.html | 0 .../oauth/internals.html | 0 .../oauth/oauth_flow.html | 0 .../oauth/oauth_settings.html | 0 .../request/async_internals.html | 0 .../request/async_request.html | 0 .../request/index.html | 0 .../request/internals.html | 0 .../request/payload_utils.html | 0 .../request/request.html | 0 .../response/index.html | 0 .../response/response.html | 0 .../util/async_utils.html | 0 .../slack_bolt => reference}/util/index.html | 0 .../slack_bolt => reference}/util/utils.html | 0 .../slack_bolt => reference}/version.html | 0 .../workflows/index.html | 0 .../workflows/step/async_step.html | 0 .../workflows/step/async_step_middleware.html | 0 .../workflows/step/index.html | 0 .../workflows/step/internals.html | 0 .../workflows/step/step.html | 0 .../workflows/step/step_middleware.html | 0 .../step/utilities/async_complete.html | 0 .../step/utilities/async_configure.html | 0 .../workflows/step/utilities/async_fail.html | 0 .../step/utilities/async_update.html | 0 .../workflows/step/utilities/complete.html | 0 .../workflows/step/utilities/configure.html | 0 .../workflows/step/utilities/fail.html | 0 .../workflows/step/utilities/index.html | 0 .../workflows/step/utilities/update.html | 0 docs/sidebars.js | 127 - docs/src/css/custom.css | 583 - docs/src/theme/NotFound/Content/index.js | 36 - docs/src/theme/NotFound/index.js | 19 - docs/static/.nojekyll | 0 docs/static/img/bolt-logo.svg | 1 - docs/static/img/bolt-py-logo.svg | 1 - docs/static/img/boltpy/bolt-favicon.png | Bin 3376 -> 0 bytes docs/static/img/boltpy/ngrok.gif | Bin 49094 -> 0 bytes docs/static/img/boltpy/request-url-config.png | Bin 168494 -> 0 bytes docs/static/img/boltpy/signing-secret.png | Bin 289939 -> 0 bytes docs/static/img/favicon.ico | Bin 24499 -> 0 bytes docs/static/img/slack-logo-on-white.png | Bin 25811 -> 0 bytes docs/static/img/slack-logo.svg | 6 - examples/getting_started/app.py | 2 +- scripts/generate_api_docs.sh | 6 +- 379 files changed, 679 insertions(+), 19452 deletions(-) delete mode 100644 .github/workflows/docs-deploy.yml delete mode 100644 docs/.gitignore delete mode 100644 docs/README.md delete mode 100644 docs/babel.config.js delete mode 100644 docs/content/concepts/custom-steps.md delete mode 100644 docs/content/concepts/web-api.md delete mode 100644 docs/docusaurus.config.js create mode 100644 docs/english/_sidebar.json rename docs/{content => english}/building-an-app.md (81%) rename docs/{content => english}/concepts/acknowledge.md (60%) rename docs/{content => english}/concepts/actions.md (74%) rename docs/{content => english}/concepts/adapters.md (97%) rename docs/{content => english}/concepts/ai-apps.md (79%) rename docs/{content => english}/concepts/app-home.md (52%) rename docs/{content => english}/concepts/async.md (97%) rename docs/{content => english}/concepts/authenticating-oauth.md (89%) rename docs/{content => english}/concepts/authorization.md (94%) rename docs/{content => english}/concepts/commands.md (74%) rename docs/{content => english}/concepts/context.md (96%) rename docs/{content => english}/concepts/custom-adapters.md (92%) rename docs/{content => english}/concepts/custom-steps-dynamic-options.md (75%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => english}/concepts/custom-steps.md (86%) rename docs/{content => english}/concepts/errors.md (90%) rename docs/{content => english}/concepts/event-listening.md (60%) rename docs/{content => english}/concepts/global-middleware.md (82%) rename docs/{content => english}/concepts/lazy-listeners.md (98%) rename docs/{content => english}/concepts/listener-middleware.md (83%) rename docs/{content => english}/concepts/logging.md (94%) rename docs/{content => english}/concepts/message-listening.md (59%) rename docs/{content => english}/concepts/message-sending.md (77%) rename docs/{content => english}/concepts/opening-modals.md (68%) rename docs/{content => english}/concepts/select-menu-options.md (67%) rename docs/{content => english}/concepts/shortcuts.md (74%) rename docs/{content => english}/concepts/socket-mode.md (78%) rename docs/{content => english}/concepts/token-rotation.md (73%) rename docs/{content => english}/concepts/updating-pushing-views.md (64%) rename docs/{content => english}/concepts/view-submissions.md (74%) create mode 100644 docs/english/concepts/web-api.md rename docs/{content => english}/getting-started.md (80%) rename docs/{content => english}/index.md (93%) rename docs/{content/concepts => english/legacy}/steps-from-apps.md (67%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/1.png (100%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/2.png (100%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/3.png (100%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/4.png (100%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/5.png (100%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/6.png (100%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/7.png (100%) rename docs/{static/img/tutorials => english/tutorial}/ai-chatbot/8.png (100%) rename docs/{content/tutorial => english/tutorial/ai-chatbot}/ai-chatbot.md (88%) rename docs/{static/img/tutorials/custom-steps-jira => english/tutorial/custom-steps-for-jira}/1.png (100%) rename docs/{static/img/tutorials/custom-steps-jira => english/tutorial/custom-steps-for-jira}/2.png (100%) rename docs/{static/img/tutorials/custom-steps-jira => english/tutorial/custom-steps-for-jira}/3.png (100%) rename docs/{static/img/tutorials/custom-steps-jira => english/tutorial/custom-steps-for-jira}/4.png (100%) rename docs/{static/img/tutorials/custom-steps-jira => english/tutorial/custom-steps-for-jira}/5.png (100%) rename docs/{static/img/tutorials/custom-steps-jira => english/tutorial/custom-steps-for-jira}/6.png (100%) rename docs/{static/img/tutorials/custom-steps-jira => english/tutorial/custom-steps-for-jira}/7.png (100%) rename docs/{content/tutorial => english/tutorial/custom-steps-for-jira}/custom-steps-for-jira.md (86%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/add-step.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/app-message.png (100%) rename docs/{content/tutorial => english/tutorial/custom-steps-workflow-builder-existing}/custom-steps-workflow-builder-existing.md (91%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/define-step.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/find-step.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/inputs.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/org-ready.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/outputs.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-existing => english/tutorial/custom-steps-workflow-builder-existing}/step-inputs.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/app-token.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/bot-token.png (100%) rename docs/{content/tutorial => english/tutorial/custom-steps-workflow-builder-new}/custom-steps-workflow-builder-new.md (90%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/install.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/manifest.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-1.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-10.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-11.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-12.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-2.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-3.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-4.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-5.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-6.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-7.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-8.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/wfb-9.png (100%) rename docs/{static/img/tutorials/custom-steps-wfb-new => english/tutorial/custom-steps-workflow-builder-new}/workflow-step.png (100%) rename docs/{content => english}/tutorial/custom-steps.md (94%) rename docs/{static/img/tutorials => english/tutorial}/modals/base_link.gif (100%) rename docs/{static/img/tutorials => english/tutorial}/modals/final_product.gif (100%) rename docs/{static/img/tutorials => english/tutorial}/modals/heart_icon.gif (100%) rename docs/{static/img/tutorials => english/tutorial}/modals/interactivity_url.png (100%) rename docs/{content/tutorial => english/tutorial/modals}/modals.md (83%) rename docs/{static/img/tutorials => english/tutorial}/modals/slash_command.png (100%) delete mode 100644 docs/footerConfig.js delete mode 100644 docs/i18n/ja-jp/README.md delete mode 100644 docs/i18n/ja-jp/code.json delete mode 100644 docs/i18n/ja-jp/docusaurus-plugin-content-docs/current.json delete mode 100644 docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/app-home.md delete mode 100644 docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/web-api.md delete mode 100644 docs/i18n/ja-jp/docusaurus-theme-classic/footer.json delete mode 100644 docs/i18n/ja-jp/docusaurus-theme-classic/navbar.json rename docs/{static/img/boltpy => img}/basic-information-page.png (100%) rename docs/{static/img/boltpy => img}/bot-token.png (100%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/acknowledge.md (69%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/actions.md (76%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/adapters.md (97%) create mode 100644 docs/japanese/concepts/app-home.md rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/assistant.md (91%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/async.md (97%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/authenticating-oauth.md (89%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/authorization.md (94%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/commands.md (72%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/context.md (96%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/custom-adapters.md (90%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/errors.md (92%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/event-listening.md (50%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/global-middleware.md (81%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/lazy-listeners.md (98%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/listener-middleware.md (83%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/logging.md (95%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/message-listening.md (55%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/message-sending.md (74%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/opening-modals.md (62%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/select-menu-options.md (68%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/shortcuts.md (77%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/socket-mode.md (86%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/token-rotation.md (67%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/updating-pushing-views.md (60%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/concepts/view-submissions.md (72%) create mode 100644 docs/japanese/concepts/web-api.md rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/getting-started.md (80%) rename docs/{i18n/ja-jp/docusaurus-plugin-content-docs/current => japanese}/legacy/steps-from-apps.md (71%) delete mode 100644 docs/navbarConfig.js delete mode 100644 docs/package-lock.json delete mode 100644 docs/package.json rename docs/{static/api-docs/slack_bolt => reference}/adapter/aiohttp/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/aiohttp/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/async_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/base_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/builtin/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/http_request.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/http_response.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/asgi/utils.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/chalice_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/chalice_lazy_listener_runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/lambda_s3_oauth_flow.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/lazy_listener_runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/aws_lambda/local_lambda_client.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/bottle/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/bottle/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/cherrypy/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/cherrypy/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/django/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/django/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/falcon/async_resource.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/falcon/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/falcon/resource.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/fastapi/async_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/fastapi/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/flask/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/flask/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/google_cloud_functions/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/google_cloud_functions/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/pyramid/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/pyramid/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/sanic/async_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/sanic/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/aiohttp/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/async_base_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/async_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/async_internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/base_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/builtin/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/websocket_client/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/socket_mode/websockets/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/starlette/async_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/starlette/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/starlette/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/tornado/async_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/tornado/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/tornado/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/wsgi/handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/wsgi/http_request.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/wsgi/http_response.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/wsgi/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/adapter/wsgi/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/app/app.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/app/async_app.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/app/async_server.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/app/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/async_app.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/authorization/async_authorize.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/authorization/async_authorize_args.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/authorization/authorize.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/authorization/authorize_args.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/authorization/authorize_result.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/authorization/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/ack/ack.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/ack/async_ack.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/ack/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/ack/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/assistant_utilities.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/async_assistant_utilities.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/thread_context/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/thread_context_store/async_store.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/thread_context_store/default_async_store.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/thread_context_store/default_store.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/thread_context_store/file/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/thread_context_store/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/assistant/thread_context_store/store.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/async_context.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/base_context.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/complete/async_complete.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/complete/complete.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/complete/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/context.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/fail/async_fail.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/fail/fail.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/fail/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/get_thread_context/async_get_thread_context.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/get_thread_context/get_thread_context.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/get_thread_context/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/respond/async_respond.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/respond/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/respond/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/respond/respond.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/save_thread_context/async_save_thread_context.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/save_thread_context/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/save_thread_context/save_thread_context.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/say/async_say.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/say/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/say/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/say/say.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_status/async_set_status.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_status/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_status/set_status.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_suggested_prompts/async_set_suggested_prompts.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_suggested_prompts/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_suggested_prompts/set_suggested_prompts.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_title/async_set_title.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_title/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/context/set_title/set_title.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/error/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/kwargs_injection/args.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/kwargs_injection/async_args.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/kwargs_injection/async_utils.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/kwargs_injection/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/kwargs_injection/utils.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/lazy_listener/async_internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/lazy_listener/async_runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/lazy_listener/asyncio_runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/lazy_listener/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/lazy_listener/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/lazy_listener/runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/lazy_listener/thread_runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/async_builtins.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/async_listener.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/async_listener_completion_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/async_listener_error_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/async_listener_start_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/asyncio_runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/builtins.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/custom_listener.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/listener.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/listener_completion_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/listener_error_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/listener_start_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener/thread_runner.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener_matcher/async_builtins.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener_matcher/async_listener_matcher.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener_matcher/builtins.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener_matcher/custom_listener_matcher.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener_matcher/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/listener_matcher/listener_matcher.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/logger/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/logger/messages.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/assistant/assistant.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/assistant/async_assistant.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/assistant/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/async_builtins.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/async_custom_middleware.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/async_middleware.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/async_middleware_error_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/attaching_function_token/async_attaching_function_token.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/attaching_function_token/attaching_function_token.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/attaching_function_token/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/async_authorization.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/async_internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/async_multi_teams_authorization.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/async_single_team_authorization.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/authorization.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/multi_teams_authorization.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/authorization/single_team_authorization.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/custom_middleware.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/ignoring_self_events/async_ignoring_self_events.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/ignoring_self_events/ignoring_self_events.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/ignoring_self_events/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/message_listener_matches/async_message_listener_matches.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/message_listener_matches/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/message_listener_matches/message_listener_matches.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/middleware.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/middleware_error_handler.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/request_verification/async_request_verification.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/request_verification/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/request_verification/request_verification.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/ssl_check/async_ssl_check.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/ssl_check/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/ssl_check/ssl_check.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/url_verification/async_url_verification.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/url_verification/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/middleware/url_verification/url_verification.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/async_callback_options.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/async_internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/async_oauth_flow.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/async_oauth_settings.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/callback_options.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/oauth_flow.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/oauth/oauth_settings.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/request/async_internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/request/async_request.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/request/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/request/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/request/payload_utils.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/request/request.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/response/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/response/response.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/util/async_utils.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/util/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/util/utils.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/version.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/async_step.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/async_step_middleware.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/internals.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/step.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/step_middleware.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/async_complete.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/async_configure.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/async_fail.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/async_update.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/complete.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/configure.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/fail.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/index.html (100%) rename docs/{static/api-docs/slack_bolt => reference}/workflows/step/utilities/update.html (100%) delete mode 100644 docs/sidebars.js delete mode 100644 docs/src/css/custom.css delete mode 100644 docs/src/theme/NotFound/Content/index.js delete mode 100644 docs/src/theme/NotFound/index.js delete mode 100644 docs/static/.nojekyll delete mode 100644 docs/static/img/bolt-logo.svg delete mode 100644 docs/static/img/bolt-py-logo.svg delete mode 100644 docs/static/img/boltpy/bolt-favicon.png delete mode 100644 docs/static/img/boltpy/ngrok.gif delete mode 100644 docs/static/img/boltpy/request-url-config.png delete mode 100644 docs/static/img/boltpy/signing-secret.png delete mode 100644 docs/static/img/favicon.ico delete mode 100644 docs/static/img/slack-logo-on-white.png delete mode 100644 docs/static/img/slack-logo.svg diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml deleted file mode 100644 index ed18c4b1d..000000000 --- a/.github/workflows/docs-deploy.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Deploy to GitHub Pages - -on: - pull_request: - branches: - - main - paths: - - "docs/**" - push: - branches: - - main - paths: - - "docs/**" - workflow_dispatch: - -jobs: - build: - name: Build Docusaurus - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - persist-credentials: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 20 - cache: npm - cache-dependency-path: docs/package-lock.json - - - name: Install dependencies - run: npm ci - working-directory: ./docs - - - name: Build website - run: npm run build - working-directory: ./docs - - - name: Upload Build Artifact - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 - with: - path: ./docs/build - - deploy: - name: Deploy to GitHub Pages - if: github.event_name != 'pull_request' - needs: build - - # Grant GITHUB_TOKEN the permissions required to make a Pages deployment - permissions: - pages: write # to deploy to Pages - id-token: write # verifies deployment is from an appropriate source - - # Deploy to the github-pages environment - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - runs-on: ubuntu-latest - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/README.md b/README.md index 7576597d5..c6b6a536c 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@
    Python Versions - + Documentation

    -A Python framework to build Slack apps in a flash with the latest platform features. Read the [getting started guide](https://tools.slack.dev/bolt-python/getting-started) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. The Python module documents are available [here](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/). +A Python framework to build Slack apps in a flash with the latest platform features. Read the [getting started guide](https://docs.slack.dev/tools/bolt-python/getting-started) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. The Python module documents are available [here](https://docs.slack.dev/tools/bolt-python/reference/). ## Setup @@ -192,7 +192,7 @@ Apps can be run the same way as the syncronous example above. If you'd prefer an ## Getting Help -[The documentation](https://tools.slack.dev/bolt-python) has more information on basic and advanced concepts for Bolt for Python. Also, all the Python module documents of this library are available [here](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/). +[The documentation](https://tools.slack.dev/bolt-python) has more information on basic and advanced concepts for Bolt for Python. Also, all the Python module documents of this library are available [here](https://tools.slack.dev/bolt-python/reference/). If you otherwise get stuck, we're here to help. The following are the best ways to get assistance working through your issue: diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 53a1610fd..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ -.docusaurus -build \ No newline at end of file diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 023d89975..000000000 --- a/docs/README.md +++ /dev/null @@ -1,129 +0,0 @@ -# tools.slack.dev/bolt-python - -This website is built using [Docusaurus](https://docusaurus.io/). 'Tis cool. - -Each Bolt/SDK has its own Docusaurus website, with matching CSS and nav/footer. There is also be a Docusaurus website of just the homepage and community tools. - -``` -website/ -├── docs/ (the good stuff. md and mdx files supported) -│ ├── getting-started.md -│ └── concepts -│ └── sending-message.md -├── i18n/ja/ (the japanese translations) -│ ├──docusaurus-theme-classic/ (footer/navbar translations) -│ └──docusaurus-plugin-content-docs/ -│ └── current/ ( file names need to exactly match **/docs/, but japanese content) -│ ├── getting-started.md -│ └── concepts -│ └── sending-message.md -├── static/ -│ ├── css/ -│ │ └── custom.css (the css for everything!) -│ ├── img/ (the pictures for the site) -│ │ ├── rory.png -│ │ └── oslo.svg -│ └── api-docs/slack_bolt (the generated reference docs with their own HTML/CSS) -│ ├── index.html -│ └── adaptor -│ └── index.html -├── src/ -│ ├── pages/ (stuff that isn't docs. This is empty for this repo!) -│ └── theme/ (only contains the 404 page) -├── docusaurus.config.js (main config file) -├── footerConfig.js (footer. go to main repo to change) -├── navbarConfig.js (navbar. go to main repo to change) -└── sidebar.js (manually set where the docs are in the sidebar.) -``` - -A cheat-sheet: -* _I want to edit a doc._ `docs/*/*.md` -* _I want to edit a Japanese doc._ `i18n/ja-jp/docusaurus-plugin-content-docs/current/*/*.md`. See the [Japanese docs README](./docs/README.md) -* _I want to change the docs sidebar._ `sidebar.js` -* _I want to change the css._ Don't use this repo, use the home repo and the changes will propagate here. -* _I want to change anything else._ `docusaurus.config.js` - ----- - -## Adding a doc - -1. Make a markdown file. Add a `# Title` or use [front matter](https://docusaurus.io/docs/next/create-doc) with `title:`. -2. Save it in `docs/folder/title.md` or `docs/title.md`, depending on if it's in a sidebar category. The nuance is just for internal organization. -3. There needs to be 1:1 docs for the sidebar. Copy the folder/file and put it in the Japanese docs: `i18n/ja/docusaurus-plugin-content-docs/current/*`. Just leave it in English if you don't speak Japanese. -4. Add the doc's path to the sidebar within `docusaurus.config.js`. Where ever makes most sense for you. -5. Test the changes ↓ - ---- - -## Running locally - -Docusaurus requires at least Node 18. You can update Node however you want. `nvm` is one way. - -Install `nvm` if you don't have it: - -``` -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash -``` - -Then grab the latest version of Node. - -``` -nvm install node -``` - -If you are running this project locally for the first time, you'll need to install the packages with the following command: - -``` -npm install -``` - -The following command starts a local development server and opens up a browser window. - -``` -npm run start -``` - -Edits to pages are reflected live — no restarting the server or reloading the page. (I'd say... 95% of the time, and 100% time if you're just editing a markdown file). The generated reference docs only load in prod! - -Remember — you're only viewing the Bolt for Python docs right now. - -#### Running locally in Japanese - -For local runs, Docusaurus treats each language as a different instance of the website. You'll want to specify the language to run the japanese site locally: - -``` -npm run start -- --locale ja-jp -``` - -Don't worry - both languages will be built/served on deployment. - ---- - -## Deploying - -The following command generates static content into the `build` directory. - -``` -$ npm run build -``` - -Then you can test out with the following command: - -``` -npm run serve -``` - -If it looks good, make a PR request! - -### Deployment to GitHub pages - -There is a GitHub action workflow set up in each repo. - -* On PR, it tests a site build. -* On Merge, it builds the site and deploys it. Site should update in a minute or two. - ---- - -## Something's broken - -Luke goofed. Open an issue please! `:bufo-appreciates-the-insight:` \ No newline at end of file diff --git a/docs/babel.config.js b/docs/babel.config.js deleted file mode 100644 index e00595dae..000000000 --- a/docs/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], -}; diff --git a/docs/content/concepts/custom-steps.md b/docs/content/concepts/custom-steps.md deleted file mode 100644 index 52e3d76e2..000000000 --- a/docs/content/concepts/custom-steps.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -title: Listening and responding to custom steps -sidebar_label: Custom Steps -lang: en -slug: /concepts/custom-steps ---- - -Your app can use the `function()` method to listen to incoming [custom step requests](https://docs.slack.dev/workflows/workflow-steps). Custom steps are used in Workflow Builder to build workflows. The method requires a step `callback_id` of type `str`. This `callback_id` must also be defined in your [Function](https://docs.slack.dev/reference/app-manifest#functions) definition. Custom steps must be finalized using the `complete()` or `fail()` listener arguments to notify Slack that your app has processed the request. - -* `complete()` requires **one** argument: `outputs` of type `dict`. It ends your custom step **successfully** and provides a dictionary containing the outputs of your custom step as per its definition. -* `fail()` requires **one** argument: `error` of type `str`. It ends your custom step **unsuccessfully** and provides a message containing information regarding why your custom step failed. - -You can reference your custom step's inputs using the `inputs` listener argument of type `dict`. - -Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn about the available listener arguments. - -```python -# This sample custom step formats an input and outputs it -@app.function("sample_custom_step") -def sample_step_callback(inputs: dict, fail: Fail, complete: Complete): - try: - message = inputs["message"] - complete( - outputs={ - "message": f":wave: You submitted the following message: \n\n>{message}" - } - ) - except Exception as e: - fail(f"Failed to handle a custom step request (error: {e})") - raise e -``` - -
    - -Example app manifest definition - - -```json -... -"functions": { - "sample_custom_step": { - "title": "Sample custom step", - "description": "Run a sample custom step", - "input_parameters": { - "message": { - "type": "string", - "title": "Message", - "description": "A message to be formatted by the custom step", - "is_required": true, - } - }, - "output_parameters": { - "message": { - "type": "string", - "title": "Messge", - "description": "A formatted message", - "is_required": true, - } - } - } -} -``` - -
    - ---- - -### Listening to custom step interactivity events - -Your app's custom steps may create interactivity points for users, for example: Post a message with a button. - -If such interaction points originate from a custom step execution, the events sent to your app representing the end-user interaction with these points are considered to be _function-scoped interactivity events_. These interactivity events can be handled by your app using the same concepts we covered earlier, such as [Listening to actions](/concepts/action-listening). - -_function-scoped interactivity events_ will contain data related to the custom step (`function_executed` event) they were spawned from, such as custom step `inputs` and access to `complete()` and `fail()` listener arguments. - -Your app can skip calling `complete()` or `fail()` in the `function()` handler method if the custom step creates an interaction point that requires user interaction before the step can end. However, in the relevant interactivity handler method, your app must invoke `complete()` or `fail()` to notify Slack that the custom step has been processed. - -You’ll notice in all interactivity handler examples, `ack()` is used. It is required to call the `ack()` function within an interactivity listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests section](/concepts/acknowledge). - -```python -# This sample custom step posts a message with a button -@app.function("custom_step_button") -def sample_step_callback(inputs, say, fail): - try: - say( - channel=inputs["user_id"], # sending a DM to this user - text="Click the button to signal the step completion", - blocks=[ - { - "type": "section", - "text": {"type": "mrkdwn", "text": "Click the button to signal step completion"}, - "accessory": { - "type": "button", - "text": {"type": "plain_text", "text": "Complete step"}, - "action_id": "sample_click", - }, - } - ], - ) - except Exception as e: - fail(f"Failed to handle a function request (error: {e})") - -# Your listener will be called every time a block element with the action_id "sample_click" is triggered -@app.action("sample_click") -def handle_sample_click(ack, body, context, client, complete, fail): - ack() - try: - # Since the button no longer works, we should remove it - client.chat_update( - channel=context.channel_id, - ts=body["message"]["ts"], - text="Congrats! You clicked the button", - ) - - # Signal that the custom step completed successfully - complete({"user_id": context.actor_user_id}) - except Exception as e: - fail(f"Failed to handle a function request (error: {e})") -``` - -
    - -Example app manifest definition - - -```json -... -"functions": { - "custom_step_button": { - "title": "Custom step with a button", - "description": "Custom step that waits for a button click", - "input_parameters": { - "user_id": { - "type": "slack#/types/user_id", - "title": "User", - "description": "The recipient of a message with a button", - "is_required": true, - } - }, - "output_parameters": { - "user_id": { - "type": "slack#/types/user_id", - "title": "User", - "description": "The user that completed the function", - "is_required": true - } - } - } -} -``` - -
    - -Learn more about responding to interactivity, see the [Slack API documentation](https://docs.slack.dev/interactivity/handling-user-interaction). diff --git a/docs/content/concepts/web-api.md b/docs/content/concepts/web-api.md deleted file mode 100644 index 18b41a029..000000000 --- a/docs/content/concepts/web-api.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: Using the Web API -lang: en -slug: /concepts/web-api ---- - -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. - -The token used to initialize Bolt can be found in the `context` object, which is required to call most Web API methods. - -:::info - -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. - -::: - -```python -@app.message("wake me up") -def say_hello(client, message): - # Unix Epoch time for September 30, 2020 11:59:59 PM - when_september_ends = 1601510399 - channel_id = message["channel"] - client.chat_scheduleMessage( - channel=channel_id, - post_at=when_september_ends, - text="Summer has come and passed" - ) -``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js deleted file mode 100644 index 0d80161e6..000000000 --- a/docs/docusaurus.config.js +++ /dev/null @@ -1,114 +0,0 @@ -import { themes as prismThemes } from 'prism-react-renderer'; -const footer = require('./footerConfig'); -const navbar = require('./navbarConfig'); - -/** @type {import('@docusaurus/types').Config} */ -const config = { - title: "Bolt for Python", - tagline: "Official frameworks, libraries, and SDKs for Slack developers", - favicon: "img/favicon.ico", - url: "https://tools.slack.dev", - baseUrl: "/bolt-python/", - organizationName: "slackapi", - projectName: "bolt-python", - - onBrokenLinks: "ignore", - onBrokenAnchors: "warn", - onBrokenMarkdownLinks: "warn", - - i18n: { - defaultLocale: "en", - locales: ["en", "ja-jp"], - }, - - presets: [ - [ - "classic", - /** @type {import('@docusaurus/preset-classic').Options} */ - ({ - docs: { - path: "content", - breadcrumbs: false, - routeBasePath: "/", // Serve the docs at the site's root - sidebarPath: "./sidebars.js", - editUrl: "https://github.com/slackapi/bolt-python/tree/main/docs", - }, - blog: false, - theme: { - customCss: "./src/css/custom.css", - }, - }), - ], - ], - - plugins: [ - "docusaurus-theme-github-codeblock", - [ - "@docusaurus/plugin-client-redirects", - { - redirects: [ - { - to: "/getting-started", - from: ["/tutorial/getting-started"], - }, - { - to: "/", - from: ["/concepts", "/concepts/basic", "/concepts/advanced"], - }, - { - to: '/concepts/actions', - from: [ - '/concepts/action-listening', - '/concepts/action-responding' - ], - }, - { - to: '/legacy/steps-from-apps', - from: [ - '/concepts/steps', - '/concepts/creating-steps', - '/concepts/adding-editing-steps', - '/concepts/saving-steps', - '/concepts/executing-steps' - ], - }, - { - to: '/concepts/ai-apps', - from: '/concepts/assistant' - } - ], - }, - ], - ], - - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - ({ - colorMode: { - respectPrefersColorScheme: true, - }, - docs: { - sidebar: { - autoCollapseCategories: true, - }, - }, - navbar, - footer, - prism: { - // switch to alucard when available in prism? - theme: prismThemes.github, - darkTheme: prismThemes.dracula, - additionalLanguages: ['bash'], - }, - codeblock: { - showGithubLink: true, - githubLinkLabel: "View on GitHub", - }, - // announcementBar: { - // id: `announcementBar`, - // content: `🎉️ Version 2.26.0 of the developer tools for the Slack automations platform is here! 🎉️ `, - // }, - }), -}; - -export default config; diff --git a/docs/english/_sidebar.json b/docs/english/_sidebar.json new file mode 100644 index 000000000..d42868543 --- /dev/null +++ b/docs/english/_sidebar.json @@ -0,0 +1,209 @@ +[ + { + "type": "doc", + "id": "tools/bolt-python/index", + "label": "Bolt for Python", + "className": "sidebar-title" + }, + "tools/bolt-python/getting-started", + { "type": "html", "value": "
    " }, + "tools/bolt-python/building-an-app", + { + "type": "category", + "label": "Slack API calls", + "items": [ + "tools/bolt-python/concepts/message-sending", + "tools/bolt-python/concepts/web-api" + ] + }, + { + "type": "category", + "label": "Events", + "items": [ + "tools/bolt-python/concepts/message-listening", + "tools/bolt-python/concepts/event-listening" + ] + }, + { + "type": "category", + "label": "App UI & Interactivity", + "items": [ + "tools/bolt-python/concepts/acknowledge", + "tools/bolt-python/concepts/shortcuts", + "tools/bolt-python/concepts/commands", + "tools/bolt-python/concepts/actions", + "tools/bolt-python/concepts/opening-modals", + "tools/bolt-python/concepts/updating-pushing-views", + "tools/bolt-python/concepts/view-submissions", + "tools/bolt-python/concepts/select-menu-options", + "tools/bolt-python/concepts/app-home" + ] + }, + "tools/bolt-python/concepts/ai-apps", + { + "type": "category", + "label": "Custom Steps", + "items": [ + "tools/bolt-python/concepts/custom-steps", + "tools/bolt-python/concepts/custom-steps-dynamic-options" + ] + }, + { + "type": "category", + "label": "App Configuration", + "items": [ + "tools/bolt-python/concepts/socket-mode", + "tools/bolt-python/concepts/errors", + "tools/bolt-python/concepts/logging", + "tools/bolt-python/concepts/async" + ] + }, + { + "type": "category", + "label": "Middleware & Context", + "items": [ + "tools/bolt-python/concepts/global-middleware", + "tools/bolt-python/concepts/listener-middleware", + "tools/bolt-python/concepts/context" + ] + }, + "tools/bolt-python/concepts/lazy-listeners", + { + "type": "category", + "label": "Adaptors", + "items": [ + "tools/bolt-python/concepts/adapters", + "tools/bolt-python/concepts/custom-adapters" + ] + }, + { + "type": "category", + "label": "Authorization & Security", + "items": [ + "tools/bolt-python/concepts/authenticating-oauth", + "tools/bolt-python/concepts/authorization", + "tools/bolt-python/concepts/token-rotation" + ] + }, + { + "type": "category", + "label": "Legacy", + "items": ["tools/bolt-python/legacy/steps-from-apps"] + }, + { "type": "html", "value": "
    " }, + { + "type": "category", + "label": "Tutorials", + "items": [ + "tools/bolt-python/tutorial/ai-chatbot/ai-chatbot", + "tools/bolt-python/tutorial/custom-steps", + "tools/bolt-python/tutorial/custom-steps-for-jira/custom-steps-for-jira", + "tools/bolt-python/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new", + "tools/bolt-python/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing", + "tools/bolt-python/tutorial/modals/modals" + ] + }, + { "type": "html", "value": "
    " }, + { + "type": "link", + "label": "Reference", + "href": "https://docs.slack.dev/tools/bolt-python/reference/index.html" + }, + { "type": "html", "value": "
    " }, + { + "type": "category", + "label": "日本語 (日本)", + "items": [ + "tools/bolt-python/ja-jp/getting-started", + { + "type": "category", + "label": "Slack API コール", + "items": [ + "tools/bolt-python/ja-jp/concepts/message-sending", + "tools/bolt-python/ja-jp/concepts/web-api" + ] + }, + { + "type": "category", + "label": "イベント API", + "items": [ + "tools/bolt-python/ja-jp/concepts/message-listening", + "tools/bolt-python/ja-jp/concepts/event-listening" + ] + }, + { + "type": "category", + "label": "インタラクティビティ & ショートカット", + "items": [ + "tools/bolt-python/ja-jp/concepts/acknowledge", + "tools/bolt-python/ja-jp/concepts/shortcuts", + "tools/bolt-python/ja-jp/concepts/commands", + "tools/bolt-python/ja-jp/concepts/actions", + "tools/bolt-python/ja-jp/concepts/opening-modals", + "tools/bolt-python/ja-jp/concepts/updating-pushing-views", + "tools/bolt-python/ja-jp/concepts/view-submissions", + "tools/bolt-python/ja-jp/concepts/select-menu-options", + "tools/bolt-python/ja-jp/concepts/app-home" + ] + }, + { + "type": "category", + "label": "App の設定", + "items": [ + "tools/bolt-python/ja-jp/concepts/socket-mode", + "tools/bolt-python/ja-jp/concepts/errors", + "tools/bolt-python/ja-jp/concepts/logging", + "tools/bolt-python/ja-jp/concepts/async" + ] + }, + { + "type": "category", + "label": "ミドルウェア & コンテキスト", + "items": [ + "tools/bolt-python/ja-jp/concepts/global-middleware", + "tools/bolt-python/ja-jp/concepts/listener-middleware", + "tools/bolt-python/ja-jp/concepts/context" + ] + }, + "tools/bolt-python/ja-jp/concepts/lazy-listeners", + { + "type": "category", + "label": "アダプター", + "items": [ + "tools/bolt-python/ja-jp/concepts/adapters", + "tools/bolt-python/ja-jp/concepts/custom-adapters" + ] + }, + { + "type": "category", + "label": "認可 & セキュリティ", + "items": [ + "tools/bolt-python/ja-jp/concepts/authenticating-oauth", + "tools/bolt-python/ja-jp/concepts/authorization", + "tools/bolt-python/ja-jp/concepts/token-rotation" + ] + }, + { + "type": "category", + "label": "レガシー(非推奨)", + "items": ["tools/bolt-python/ja-jp/legacy/steps-from-apps"] + } + ] + }, + { "type": "html", "value": "
    " }, + { + "type": "link", + "label": "Release notes", + "href": "https://github.com/slackapi/bolt-python/releases" + }, + { + "type": "link", + "label": "Code on GitHub", + "href": "https://github.com/SlackAPI/bolt-python" + }, + { + "type": "link", + "label": "Contributors Guide", + "href": "https://github.com/SlackAPI/bolt-python/blob/main/.github/contributing.md" + } +] diff --git a/docs/content/building-an-app.md b/docs/english/building-an-app.md similarity index 81% rename from docs/content/building-an-app.md rename to docs/english/building-an-app.md index deb3146b9..87b26163b 100644 --- a/docs/content/building-an-app.md +++ b/docs/english/building-an-app.md @@ -1,5 +1,4 @@ --- -title: Building an App with Bolt for Python sidebar_label: Building an App --- @@ -24,31 +23,31 @@ After you fill out an app name (_you can change it later_) and pick a workspace This page contains an overview of your app in addition to important credentials you'll need later. -![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") +![Basic Information page](/img/bolt-python/basic-information-page.png "Basic Information page") Look around, add an app icon and description, and then let's start configuring your app 🔩 --- ### Tokens and installing apps {#tokens-and-installing-apps} -Slack apps use [OAuth to manage access to Slack's APIs](https://docs.slack.dev/authentication/installing-with-oauth). When an app is installed, you'll receive a token that the app can use to call API methods. +Slack apps use [OAuth to manage access to Slack's APIs](/authentication/installing-with-oauth). When an app is installed, you'll receive a token that the app can use to call API methods. There are three main token types available to a Slack app: user (`xoxp`), bot (`xoxb`), and app-level (`xapp`) tokens. -- [User tokens](https://docs.slack.dev/authentication/tokens#user) allow you to call API methods on behalf of users after they install or authenticate the app. There may be several user tokens for a single workspace. -- [Bot tokens](https://docs.slack.dev/authentication/tokens#bot) are associated with bot users, and are only granted once in a workspace where someone installs the app. The bot token your app uses will be the same no matter which user performed the installation. Bot tokens are the token type that _most_ apps use. -- [App-level tokens](https://docs.slack.dev/authentication/tokens#app-level) represent your app across organizations, including installations by all individual users on all workspaces in a given organization and are commonly used for creating WebSocket connections to your app. +- [User tokens](/authentication/tokens#user) allow you to call API methods on behalf of users after they install or authenticate the app. There may be several user tokens for a single workspace. +- [Bot tokens](/authentication/tokens#bot) are associated with bot users, and are only granted once in a workspace where someone installs the app. The bot token your app uses will be the same no matter which user performed the installation. Bot tokens are the token type that _most_ apps use. +- [App-level tokens](/authentication/tokens#app-level) represent your app across organizations, including installations by all individual users on all workspaces in a given organization and are commonly used for creating WebSocket connections to your app. We're going to use bot and app-level tokens for this guide. 1. Navigate to **OAuth & Permissions** on the left sidebar and scroll down to the **Bot Token Scopes** section. Click **Add an OAuth Scope**. -2. For now, we'll just add one scope: [`chat:write`](https://docs.slack.dev/reference/scopes/chat.write). This grants your app the permission to post messages in channels it's a member of. +2. For now, we'll just add one scope: [`chat:write`](/reference/scopes/chat.write). This grants your app the permission to post messages in channels it's a member of. 3. Scroll up to the top of the **OAuth & Permissions** page and click **Install App to Workspace**. You'll be led through Slack's OAuth UI, where you should allow your app to be installed to your development workspace. 4. Once you authorize the installation, you'll land on the **OAuth & Permissions** page and see a **Bot User OAuth Access Token**. -![OAuth Tokens](/img/boltpy/bot-token.png "Bot OAuth Token") +![OAuth Tokens](/img/bolt-python/bot-token.png "Bot OAuth Token") 5. Head over to **Basic Information** and scroll down under the App Token section and click **Generate Token and Scopes** to generate an app-level token. Add the `connections:write` scope to this token and save the generated `xapp` token. @@ -56,7 +55,7 @@ We're going to use bot and app-level tokens for this guide. :::tip[Not sharing is sometimes caring] -Treat your tokens like passwords and [keep them safe](https://docs.slack.dev/authentication/best-practices-for-security). Your app uses tokens to post and retrieve information from Slack workspaces. +Treat your tokens like passwords and [keep them safe](/authentication/best-practices-for-security). Your app uses tokens to post and retrieve information from Slack workspaces. ::: @@ -104,7 +103,7 @@ $ export SLACK_APP_TOKEN= :::warning[Keep it secret. Keep it safe.] -Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](https://docs.slack.dev/authentication/best-practices-for-security). +Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](/authentication/best-practices-for-security). ::: @@ -142,9 +141,9 @@ Your app should let you know that it's up and running. 🎉 ### Setting up events {#setting-up-events} Your app behaves similarly to people on your team — it can post messages, add emoji reactions, and listen and respond to events. -To listen for events happening in a Slack workspace (like when a message is posted or when a reaction is posted to a message) you'll use the [Events API to subscribe to event types](https://docs.slack.dev/apis/events-api/). +To listen for events happening in a Slack workspace (like when a message is posted or when a reaction is posted to a message) you'll use the [Events API to subscribe to event types](/apis/events-api/). -For those just starting, we recommend using [Socket Mode](https://docs.slack.dev/apis/events-api/using-socket-mode). Socket Mode allows your app to use the Events API and interactive features without exposing a public HTTP Request URL. This can be helpful during development, or if you're receiving requests from behind a firewall. +For those just starting, we recommend using [Socket Mode](/apis/events-api/using-socket-mode). Socket Mode allows your app to use the Events API and interactive features without exposing a public HTTP Request URL. This can be helpful during development, or if you're receiving requests from behind a firewall. That being said, you're welcome to set up an app with a public HTTP Request URL. HTTP is more useful for apps being deployed to hosting environments to respond within a large corporate Slack workspaces/organization, or apps intended for distribution via the Slack Marketplace. @@ -169,11 +168,11 @@ When an event occurs, Slack will send your app some information about the event, 1. Go back to your app configuration page (click on the app [from your app management page](https://api.slack.com/apps)). Click **Event Subscriptions** on the left sidebar. Toggle the switch labeled **Enable Events**. -2. Add your Request URL. Slack will send HTTP POST requests corresponding to events to this [Request URL](https://docs.slack.dev/apis/events-api/#subscribing) endpoint. Bolt uses the `/slack/events` path to listen to all incoming requests (whether shortcuts, events, or interactivity payloads). When configuring your Request URL within your app configuration, you'll append `/slack/events`, e.g. `https:///slack/events`. 💡 As long as your Bolt app is still running, your URL should become verified. +2. Add your Request URL. Slack will send HTTP POST requests corresponding to events to this [Request URL](/apis/events-api/#subscribing) endpoint. Bolt uses the `/slack/events` path to listen to all incoming requests (whether shortcuts, events, or interactivity payloads). When configuring your Request URL within your app configuration, you'll append `/slack/events`, e.g. `https:///slack/events`. 💡 As long as your Bolt app is still running, your URL should become verified. :::tip[Using proxy services] -For local development, you can use a proxy service like ngrok to create a public URL and tunnel requests to your development environment. Refer to [ngrok's getting started guide](https://ngrok.com/docs#getting-started-expose) on how to create this tunnel. And when you get to hosting your app, we've collected some of the most common hosting providers Slack developers use to host their apps [on our API site](https://docs.slack.dev/distribution/hosting-slack-apps/). +For local development, you can use a proxy service like ngrok to create a public URL and tunnel requests to your development environment. Refer to [ngrok's getting started guide](https://ngrok.com/docs#getting-started-expose) on how to create this tunnel. And when you get to hosting your app, we've collected some of the most common hosting providers Slack developers use to host their apps [on our API site](/app-management/hosting-slack-apps). ::: @@ -181,10 +180,10 @@ For local development, you can use a proxy service like ngrok to create a public
    Navigate to **Event Subscriptions** on the left sidebar and toggle to enable. Under **Subscribe to Bot Events**, you can add events for your bot to respond to. There are four events related to messages: -- [`message.channels`](https://docs.slack.dev/reference/events/message.channels) listens for messages in public channels that your app is added to. -- [`message.groups`](https://docs.slack.dev/reference/events/message.groups) listens for messages in 🔒 private channels that your app is added to. -- [`message.im`](https://docs.slack.dev/reference/events/message.im) listens for messages in your app's DMs with users. -- [`message.mpim`](https://docs.slack.dev/reference/events/message.mpim) listens for messages in multi-person DMs that your app is added to. +- [`message.channels`](/reference/events/message.channels) listens for messages in public channels that your app is added to. +- [`message.groups`](/reference/events/message.groups) listens for messages in 🔒 private channels that your app is added to. +- [`message.im`](/reference/events/message.im) listens for messages in your app's DMs with users. +- [`message.mpim`](/reference/events/message.mpim) listens for messages in multi-person DMs that your app is added to. If you want your bot to listen to messages from everywhere it is added to, choose all four message events. After you’ve selected the events you want your bot to listen to, click the green **Save Changes** button. @@ -208,7 +207,7 @@ app = App(token=os.environ.get("SLACK_BOT_TOKEN")) # Listens to incoming messages that contain "hello" # To learn available listener arguments, -# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# visit https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered @@ -234,7 +233,7 @@ app = App( # Listens to incoming messages that contain "hello" # To learn available listener arguments, -# visit https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# visit https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered @@ -268,9 +267,9 @@ With Socket Mode on, basic interactivity is enabled by default, so no further ac Similar to events, you'll need to specify a URL for Slack to send the action (such as _user clicked a button_). Back on your app configuration page, click on **Interactivity & Shortcuts** on the left side. You'll see that there's another **Request URL** box. -:::tip +:::tip[By default, Bolt is configured to use the same endpoint for interactive components that it uses for events, so use the same request URL as above (for example, `https://8e8ec2d7.ngrok.io/slack/events`).] -By default, Bolt is configured to use the same endpoint for interactive components that it uses for events, so use the same request URL as above (for example, `https://8e8ec2d7.ngrok.io/slack/events`). Press the **Save Changes** button in the lower right hand corner, and that's it. Your app is set up to handle interactivity! +Press the **Save Changes** button in the lower right hand corner, and that's it. Your app is set up to handle interactivity! ::: @@ -476,8 +475,8 @@ Now that you have a basic app up and running, you can start exploring how to mak * Read through the concepts pages to learn about the different methods and features your Bolt app has access to. -* Explore the different events your bot can listen to with the [`app.event()`](/concepts/event-listening) method. All of the events are listed [on the API docs site](https://docs.slack.dev/reference/events). +* Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. All of the events are listed [on the API docs site](/reference/events). -* Bolt allows you to [call Web API methods](/concepts/web-api) with the client attached to your app. There are [over 200 methods](https://docs.slack.dev/reference/methods) on our API site. +* Bolt allows you to [call Web API methods](/tools/bolt-python/concepts/web-api) with the client attached to your app. There are [over 200 methods](/reference/methods) on our API site. -* Learn more about the different token types [on the API docs site](https://docs.slack.dev/authentication/tokens). Your app may need different tokens depending on the actions you want it to perform. +* Learn more about the different token types [on the API docs site](/authentication/tokens). Your app may need different tokens depending on the actions you want it to perform. diff --git a/docs/content/concepts/acknowledge.md b/docs/english/concepts/acknowledge.md similarity index 60% rename from docs/content/concepts/acknowledge.md rename to docs/english/concepts/acknowledge.md index 5e5c0ed25..7d91e0851 100644 --- a/docs/content/concepts/acknowledge.md +++ b/docs/english/concepts/acknowledge.md @@ -1,22 +1,16 @@ ---- -title: Acknowledging requests -lang: en -slug: /concepts/acknowledge ---- +# Acknowledging requests Actions, commands, shortcuts, options requests, and view submissions must **always** be acknowledged using the `ack()` function. This lets Slack know that the request was received so that it may update the Slack user interface accordingly. -Depending on the type of request, your acknowledgement may be different. For example, when acknowledging a menu selection associated with an external data source, you would call `ack()` with a list of relevant [options](https://docs.slack.dev/reference/block-kit/composition-objects/option-object/). When acknowledging a view submission, you may supply a `response_action` as part of your acknowledgement to [update the view](/concepts/view_submissions). +Depending on the type of request, your acknowledgement may be different. For example, when acknowledging a menu selection associated with an external data source, you would call `ack()` with a list of relevant [options](/reference/block-kit/composition-objects/option-object/). When acknowledging a view submission, you may supply a `response_action` as part of your acknowledgement to [update the view](/tools/bolt-python/concepts/view-submissions). We recommend calling `ack()` right away before initiating any time-consuming processes such as fetching information from your database or sending a new message, since you only have 3 seconds to respond before Slack registers a timeout error. -:::info - -When working in a FaaS / serverless environment, our guidelines for when to `ack()` are different. See the section on [Lazy listeners (FaaS)](/concepts/lazy-listeners) for more detail on this. +:::info[When working in a FaaS / serverless environment, our guidelines for when to `ack()` are different. See the section on [Lazy listeners (FaaS)](/tools/bolt-python/concepts/lazy-listeners) for more detail on this.] ::: -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Example of responding to an external_select options request @app.options("menu_selection") diff --git a/docs/content/concepts/actions.md b/docs/english/concepts/actions.md similarity index 74% rename from docs/content/concepts/actions.md rename to docs/english/concepts/actions.md index 018642b4a..d7dfa6ba1 100644 --- a/docs/content/concepts/actions.md +++ b/docs/english/concepts/actions.md @@ -1,8 +1,4 @@ ---- -title: Listening & responding to actions -lang: en -slug: /concepts/actions ---- +# Listening & responding to actions Your app can listen and respond to user actions, like button clicks, and menu selects, using the `action` method. @@ -10,9 +6,9 @@ Your app can listen and respond to user actions, like button clicks, and menu se Actions can be filtered on an `action_id` parameter of type `str` or `re.Pattern`. The `action_id` parameter acts as a unique identifier for interactive components on the Slack platform. -You'll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests guide](/concepts/acknowledge). +You'll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests guide](/tools/bolt-python/concepts/acknowledge). -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Your listener will be called every time a block element with the action_id "approve_button" is triggered @@ -49,7 +45,7 @@ There are two main ways to respond to actions. The first (and most common) way i The second way to respond to actions is using `respond()`, which is a utility to use the `response_url` associated with the action. -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Your listener will be called every time an interactive component with the action_id “approve_button” is triggered @@ -62,7 +58,7 @@ def approve_request(ack, say): ### Using `respond()` method -Since `respond()` is a utility for calling the `response_url`, it behaves in the same way. You can pass [all the message payload properties](https://docs.slack.dev/messaging/#payloads) as keyword arguments along with optional properties like `response_type` (which has a value of `"in_channel"` or `"ephemeral"`), `replace_original`, `delete_original`, `unfurl_links`, and `unfurl_media`. With that, your app can send a new message payload that will be published back to the source of the original interaction. +Since `respond()` is a utility for calling the `response_url`, it behaves in the same way. You can pass [all the message payload properties](/messaging/#payloads) as keyword arguments along with optional properties like `response_type` (which has a value of `"in_channel"` or `"ephemeral"`), `replace_original`, `delete_original`, `unfurl_links`, and `unfurl_media`. With that, your app can send a new message payload that will be published back to the source of the original interaction. ```python # Listens to actions triggered with action_id of “user_select” diff --git a/docs/content/concepts/adapters.md b/docs/english/concepts/adapters.md similarity index 97% rename from docs/content/concepts/adapters.md rename to docs/english/concepts/adapters.md index ad4303b15..321dae0ab 100644 --- a/docs/content/concepts/adapters.md +++ b/docs/english/concepts/adapters.md @@ -1,8 +1,4 @@ ---- -title: Adapters -lang: en -slug: /concepts/adapters ---- +# Adapters Adapters are responsible for handling and parsing incoming requests from Slack to conform to [`BoltRequest`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/request/request.py), then dispatching those requests to your Bolt app. diff --git a/docs/content/concepts/ai-apps.md b/docs/english/concepts/ai-apps.md similarity index 79% rename from docs/content/concepts/ai-apps.md rename to docs/english/concepts/ai-apps.md index a8da2bf1f..b294c6688 100644 --- a/docs/content/concepts/ai-apps.md +++ b/docs/english/concepts/ai-apps.md @@ -1,40 +1,36 @@ ---- -title: Using AI in Apps -lang: en -slug: /concepts/ai-apps ---- +# Using AI in Apps -:::info This feature requires a paid plan +:::info[This feature requires a paid plan] If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. ::: -The Agents & AI Apps feature comprises a unique messaging experience for Slack. If you're unfamiliar with using the Agents & AI Apps feature within Slack, you'll want to read the [API documentation on the subject](https://docs.slack.dev/ai/). Then come back here to implement them with Bolt! +The Agents & AI Apps feature comprises a unique messaging experience for Slack. If you're unfamiliar with using the Agents & AI Apps feature within Slack, you'll want to read the [API documentation on the subject](/ai/). Then come back here to implement them with Bolt! ## Configuring your app to support AI features {#configuring-your-app} 1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & AI Apps** feature. 2. Within the App Settings **OAuth & Permissions** page, add the following scopes: -* [`assistant:write`](https://docs.slack.dev/reference/scopes/assistant.write) -* [`chat:write`](https://docs.slack.dev/reference/scopes/chat.write) -* [`im:history`](https://docs.slack.dev/reference/scopes/im.history) +* [`assistant:write`](/reference/scopes/assistant.write) +* [`chat:write`](/reference/scopes/chat.write) +* [`im:history`](/reference/scopes/im.history) 3. Within the App Settings **Event Subscriptions** page, subscribe to the following events: -* [`assistant_thread_started`](https://docs.slack.dev/reference/events/assistant_thread_started) -* [`assistant_thread_context_changed`](https://docs.slack.dev/reference/events/assistant_thread_context_changed) -* [`message.im`](https://docs.slack.dev/reference/events/message.im) +* [`assistant_thread_started`](/reference/events/assistant_thread_started) +* [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) +* [`message.im`](/reference/events/message.im) -:::info -You _could_ go it alone and [listen](event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events (see implementation details below) in order to implement the AI features in your app. That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! -::: +:::info[You _could_ implement your own AI app by [listening](event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events (see implementation details below).] + +That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! ## The `Assistant` class instance {#assistant-class} The `Assistant` class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. A typical flow would look like: -1. [The user starts a thread](#handling-a-new-thread). The `Assistant` class handles the incoming [`assistant_thread_started`](https://docs.slack.dev/reference/events/assistant_thread_started) event. -2. [The thread context may change at any point](#handling-thread-context-changes). The `Assistant` class can handle any incoming [`assistant_thread_context_changed`](https://docs.slack.dev/reference/events/assistant_thread_context_changed) events. The class also provides a default context store to keep track of thread context changes as the user moves through Slack. -3. [The user responds](#handling-the-user-response). The `Assistant` class handles the incoming [`message.im`](https://docs.slack.dev/reference/events/message.im) event. +1. [The user starts a thread](#handling-a-new-thread). The `Assistant` class handles the incoming [`assistant_thread_started`](/reference/events/assistant_thread_started) event. +2. [The thread context may change at any point](#handling-thread-context-changes). The `Assistant` class can handle any incoming [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) events. The class also provides a default context store to keep track of thread context changes as the user moves through Slack. +3. [The user responds](#handling-the-user-response). The `Assistant` class handles the incoming [`message.im`](/reference/events/message.im) event. ```python @@ -97,25 +93,25 @@ def respond_in_assistant_thread( app.use(assistant) ``` -While the `assistant_thread_started` and `assistant_thread_context_changed` events do provide Slack-client thread context information, the `message.im` event does not. Any subsequent user message events won't contain thread context data. For that reason, Bolt not only provides a way to store thread context — the `threadContextStore` property — but it also provides an instance that is utilized by default. This implementation relies on storing and retrieving [message metadata](https://docs.slack.dev/messaging/message-metadata/) as the user interacts with the app. +While the `assistant_thread_started` and `assistant_thread_context_changed` events do provide Slack-client thread context information, the `message.im` event does not. Any subsequent user message events won't contain thread context data. For that reason, Bolt not only provides a way to store thread context — the `threadContextStore` property — but it also provides an instance that is utilized by default. This implementation relies on storing and retrieving [message metadata](/messaging/message-metadata/) as the user interacts with the app. If you do provide your own `threadContextStore` property, it must feature `get` and `save` methods. -:::tip -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. +:::tip[Refer to the [module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments.] ::: ## Handling a new thread {#handling-a-new-thread} -When the user opens a new thread with your AI-enabled app, the [`assistant_thread_started`](https://docs.slack.dev/reference/events/assistant_thread_started) event will be sent to your app. +When the user opens a new thread with your AI-enabled app, the [`assistant_thread_started`](/reference/events/assistant_thread_started) event will be sent to your app. + +:::tip[When a user opens an app thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data.] -:::tip -When a user opens an app thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data. You can grab that info by using the `get_thread_context` utility, as subsequent user message event payloads won't include the channel info. +You can grab that info by using the `get_thread_context` utility, as subsequent user message event payloads won't include the channel info. ::: ### Block Kit interactions in the app thread {#block-kit-interactions} -For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](https://docs.slack.dev/messaging/message-metadata/) to trigger subsequent interactions with the user. +For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](/messaging/message-metadata/) to trigger subsequent interactions with the user. For example, an app can display a button such as "Summarize the referring channel" in the initial reply. When the user clicks the button and submits detailed information (such as the number of messages, days to check, purpose of the summary, etc.), the app can handle that information and post a message that describes the request with structured metadata. @@ -241,9 +237,9 @@ def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: ## Handling thread context changes {#handling-thread-context-changes} -When the user switches channels, the [`assistant_thread_context_changed`](https://docs.slack.dev/reference/events/assistant_thread_context_changed) event will be sent to your app. +When the user switches channels, the [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) event will be sent to your app. -If you use the built-in `Assistant` middleware without any custom configuration, the updated context data is automatically saved as [message metadata](https://docs.slack.dev/messaging/message-metadata/) of the first reply from the app. +If you use the built-in `Assistant` middleware without any custom configuration, the updated context data is automatically saved as [message metadata](/messaging/message-metadata/) of the first reply from the app. As long as you use the built-in approach, you don't need to store the context data within a datastore. The downside of this default behavior is the overhead of additional calls to the Slack API. These calls include those to `conversations.history`, which are used to look up the stored message metadata that contains the thread context (via `get_thread_context`). @@ -256,14 +252,14 @@ assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) ## Handling the user response {#handling-the-user-response} -When the user messages your app, the [`message.im`](https://docs.slack.dev/reference/events/message.im) event will be sent to your app. +When the user messages your app, the [`message.im`](/reference/events/message.im) event will be sent to your app. -Messages sent to the app do not contain a [subtype](https://docs.slack.dev/reference/events/message#subtypes) and must be deduced based on their shape and any provided [message metadata](https://docs.slack.dev/messaging/message-metadata/). +Messages sent to the app do not contain a [subtype](/reference/events/message#subtypes) and must be deduced based on their shape and any provided [message metadata](/messaging/message-metadata/). There are three utilities that are particularly useful in curating the user experience: -* [`say`](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/#slack_bolt.Say) -* [`setTitle`](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/#slack_bolt.SetTitle) -* [`setStatus`](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/#slack_bolt.SetStatus) +* [`say`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.Say) +* [`setTitle`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetTitle) +* [`setStatus`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetStatus) ```python ... diff --git a/docs/content/concepts/app-home.md b/docs/english/concepts/app-home.md similarity index 52% rename from docs/content/concepts/app-home.md rename to docs/english/concepts/app-home.md index d29bfc4d6..8b0e2cf11 100644 --- a/docs/content/concepts/app-home.md +++ b/docs/english/concepts/app-home.md @@ -1,14 +1,10 @@ ---- -title: Publishing views to App Home -lang: en -slug: /concepts/app-home ---- +# Publishing views to App Home -[Home tabs](https://docs.slack.dev/surfaces/app-home) are customizable surfaces accessible via the sidebar and search that allow apps to display views on a per-user basis. After enabling App Home within your app configuration, home tabs can be published and updated by passing a `user_id` and [view payload](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission) to the [`views.publish`](https://docs.slack.dev/reference/methods/views.publis) method. +[Home tabs](/surfaces/app-home) are customizable surfaces accessible via the sidebar and search that allow apps to display views on a per-user basis. After enabling App Home within your app configuration, home tabs can be published and updated by passing a `user_id` and [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission) to the [`views.publish`](/reference/methods/views.publish) method. -You can subscribe to the [`app_home_opened`](https://docs.slack.dev/reference/events/app_home_opened) event to listen for when users open your App Home. +You can subscribe to the [`app_home_opened`](/reference/events/app_home_opened) event to listen for when users open your App Home. -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python @app.event("app_home_opened") def update_home_tab(client, event, logger): @@ -32,7 +28,7 @@ def update_home_tab(client, event, logger): "type": "section", "text": { "type": "mrkdwn", - "text": "Learn how home tabs can be more useful and interactive ." + "text": "Learn how home tabs can be more useful and interactive ." } } ] diff --git a/docs/content/concepts/async.md b/docs/english/concepts/async.md similarity index 97% rename from docs/content/concepts/async.md rename to docs/english/concepts/async.md index 197377f93..e6ae28fc6 100644 --- a/docs/content/concepts/async.md +++ b/docs/english/concepts/async.md @@ -1,8 +1,4 @@ ---- -title: Using async (asyncio) -lang: en -slug: /concepts/async ---- +# Using async (asyncio) To use the async version of Bolt, you can import and initialize an `AsyncApp` instance (rather than `App`). `AsyncApp` relies on [AIOHTTP](https://docs.aiohttp.org) to make API requests, which means you'll need to install `aiohttp` (by adding to `requirements.txt` or running `pip install aiohttp`). diff --git a/docs/content/concepts/authenticating-oauth.md b/docs/english/concepts/authenticating-oauth.md similarity index 89% rename from docs/content/concepts/authenticating-oauth.md rename to docs/english/concepts/authenticating-oauth.md index 1fabe3522..88b422949 100644 --- a/docs/content/concepts/authenticating-oauth.md +++ b/docs/english/concepts/authenticating-oauth.md @@ -1,18 +1,14 @@ ---- -title: Authenticating with OAuth -lang: en -slug: /concepts/authenticating-oauth ---- +# Authenticating with OAuth -Slack apps installed on multiple workspaces will need to implement OAuth, then store installation information (like access tokens) securely. By providing `client_id`, `client_secret`, `scopes`, `installation_store`, and `state_store` when initializing App, Bolt for Python will handle the work of setting up OAuth routes and verifying state. If you're implementing a custom adapter, you can make use of our [OAuth library](https://tools.slack.dev/python-slack-sdk/oauth/), which is what Bolt for Python uses under the hood. +Slack apps installed on multiple workspaces will need to implement OAuth, then store installation information (like access tokens) securely. By providing `client_id`, `client_secret`, `scopes`, `installation_store`, and `state_store` when initializing App, Bolt for Python will handle the work of setting up OAuth routes and verifying state. If you're implementing a custom adapter, you can make use of our [OAuth library](/tools/python-slack-sdk/oauth/), which is what Bolt for Python uses under the hood. Bolt for Python will create a **Redirect URL** `slack/oauth_redirect`, which Slack uses to redirect users after they complete your app's installation flow. You will need to add this **Redirect URL** in your app configuration settings under **OAuth and Permissions**. This path can be configured in the `OAuthSettings` argument described below. Bolt for Python will also create a `slack/install` route, where you can find an **Add to Slack** button for your app to perform direct installs of your app. If you need any additional authorizations (user tokens) from users inside a team when your app is already installed or a reason to dynamically generate an install URL, you can pass your own custom URL generator to `oauth_settings` as `authorize_url_generator`. -Bolt for Python automatically includes support for [org wide installations](https://docs.slack.dev/enterprise-grid/) in version `1.1.0+`. Org wide installations can be enabled in your app configuration settings under **Org Level Apps**. +Bolt for Python automatically includes support for [org wide installations](/enterprise-grid/) in version `1.1.0+`. Org wide installations can be enabled in your app configuration settings under **Org Level Apps**. -To learn more about the OAuth installation flow with Slack, [read the API documentation](https://docs.slack.dev/authentication/installing-with-oauth). +To learn more about the OAuth installation flow with Slack, [read the API documentation](/authentication/installing-with-oauth). ```python import os diff --git a/docs/content/concepts/authorization.md b/docs/english/concepts/authorization.md similarity index 94% rename from docs/content/concepts/authorization.md rename to docs/english/concepts/authorization.md index 4c293b5c2..242a86b39 100644 --- a/docs/content/concepts/authorization.md +++ b/docs/english/concepts/authorization.md @@ -1,12 +1,8 @@ ---- -title: Authorization -lang: en -slug: /concepts/authorization ---- +# Authorization Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack request. -Apps installed on a single workspace can simply pass their bot token into the `App` constructor using the `token` parameter. However, if your app will be installed on multiple workspaces, you have two options. The easier option is to use the built-in OAuth support. This will handle setting up OAuth routes and verifying state. Read the section on [authenticating with OAuth](/concepts/authenticating-oauth) for details. +Apps installed on a single workspace can simply pass their bot token into the `App` constructor using the `token` parameter. However, if your app will be installed on multiple workspaces, you have two options. The easier option is to use the built-in OAuth support. This will handle setting up OAuth routes and verifying state. Read the section on [authenticating with OAuth](/tools/bolt-python/concepts/authenticating-oauth) for details. For a more custom solution, you can set the `authorize` parameter to a function upon `App` instantiation. The `authorize` function should return [an instance of `AuthorizeResult`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/authorization/authorize_result.py), which contains information about who and where the request is coming from. diff --git a/docs/content/concepts/commands.md b/docs/english/concepts/commands.md similarity index 74% rename from docs/content/concepts/commands.md rename to docs/english/concepts/commands.md index 4c99f6628..81167fb83 100644 --- a/docs/content/concepts/commands.md +++ b/docs/english/concepts/commands.md @@ -1,18 +1,14 @@ ---- -title: Listening & responding to commands -lang: en -slug: /concepts/commands ---- +# Listening & responding to commands Your app can use the `command()` method to listen to incoming slash command requests. The method requires a `command_name` of type `str`. Commands must be acknowledged with `ack()` to inform Slack your app has received the request. -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. +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](/tools/bolt-python/concepts/actions) section. When setting up commands within your app configuration, you'll append `/slack/events` to your request URL. -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # The echo command simply echoes on command @app.command("/echo") diff --git a/docs/content/concepts/context.md b/docs/english/concepts/context.md similarity index 96% rename from docs/content/concepts/context.md rename to docs/english/concepts/context.md index cf7fa45f1..fb134c896 100644 --- a/docs/content/concepts/context.md +++ b/docs/english/concepts/context.md @@ -1,8 +1,4 @@ ---- -title: Adding context -lang: en -slug: /concepts/context ---- +# Adding context All listeners have access to a `context` dictionary, which can be used to enrich requests with additional information. Bolt automatically attaches information that is included in the incoming request, like `user_id`, `team_id`, `channel_id`, and `enterprise_id`. diff --git a/docs/content/concepts/custom-adapters.md b/docs/english/concepts/custom-adapters.md similarity index 92% rename from docs/content/concepts/custom-adapters.md rename to docs/english/concepts/custom-adapters.md index 55c73130d..62532e7cd 100644 --- a/docs/content/concepts/custom-adapters.md +++ b/docs/english/concepts/custom-adapters.md @@ -1,10 +1,6 @@ ---- -title: Custom adapters -lang: en -slug: /concepts/custom-adapters ---- +# Custom adapters -[Adapters](/concepts/adapters) are flexible and can be adjusted based on the framework you prefer. There are two necessary components of adapters: +[Adapters](/tools/bolt-python/concepts/adapters) are flexible and can be adjusted based on the framework you prefer. There are two necessary components of adapters: - `__init__(app: App)`: Constructor that accepts and stores an instance of the Bolt `App`. - `handle(req: Request)`: Function (typically named `handle()`) that receives incoming Slack requests, parses them to conform to an instance of [`BoltRequest`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/request/request.py), then dispatches them to the stored Bolt app. diff --git a/docs/content/concepts/custom-steps-dynamic-options.md b/docs/english/concepts/custom-steps-dynamic-options.md similarity index 75% rename from docs/content/concepts/custom-steps-dynamic-options.md rename to docs/english/concepts/custom-steps-dynamic-options.md index cab3f7a61..9a152daa0 100644 --- a/docs/content/concepts/custom-steps-dynamic-options.md +++ b/docs/english/concepts/custom-steps-dynamic-options.md @@ -2,7 +2,7 @@ ## Background {#background} -[Legacy steps from apps](https://docs.slack.dev/changelog/2023-08-workflow-steps-from-apps-step-back) previously enabled Slack apps to create and process custom workflow steps, which could then be shared and used by anyone in Workflow Builder. To support your transition away from them, custom steps used as dynamic options are available. These allow you to use data defined when referencing the step in Workflow Builder as inputs to the step. +[Legacy steps from apps](/changelog/2023-08-workflow-steps-from-apps-step-back) previously enabled Slack apps to create and process custom workflow steps, which could then be shared and used by anyone in Workflow Builder. To support your transition away from them, custom steps used as dynamic options are available. These allow you to use data defined when referencing the step in Workflow Builder as inputs to the step. ## Example use case {#use-case} @@ -88,13 +88,13 @@ The `inputs` attribute defines the parameters to be passed as inputs to the step The following format can be used to reference any input parameter defined by the step: `{{input_parameters.}}`. -In addition, the `{{client.query}}` parameter can be used as a placeholder for an input value. The `{{client.builder_context}}` parameter will inject the [`slack#/types/user_context`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types/#usercontext) of the user building the workflow as the value to the input parameter. +In addition, the `{{client.query}}` parameter can be used as a placeholder for an input value. The `{{client.builder_context}}` parameter will inject the [`slack#/types/user_context`](/tools/deno-slack-sdk/reference/slack-types/#usercontext) of the user building the workflow as the value to the input parameter. ### Types of dynamic options UIs {#dynamic-option-UIs} The above example demonstrates one possible UI to be rendered for builders: a single-select drop-down menu of dynamic options. However, dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. -The type is dictated by the output parameter of the custom step used as a dynamic option. In order to use a custom step in a dynamic option context, its output must adhere to a defined interface, that is, it must have an `options` parameter of type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field), as shown in the following code snippet. +The type is dictated by the output parameter of the custom step used as a dynamic option. In order to use a custom step in a dynamic option context, its output must adhere to a defined interface, that is, it must have an `options` parameter of type [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field), as shown in the following code snippet. ```js "output_parameters": { @@ -109,9 +109,9 @@ The type is dictated by the output parameter of the custom step used as a dynami #### Drop-down menus {#drop-down} -Your dynamic input parameter can be rendered as a drop-down menu, which will use the options obtained from a custom step with an `options` output parameter of the type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select). +Your dynamic input parameter can be rendered as a drop-down menu, which will use the options obtained from a custom step with an `options` output parameter of the type [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select). -The drop-down menu UI component can be rendered in two ways: single-select, or multi-select. To render the dynamic input as a single-select menu, the input parameter defining the dynamic option must be of the type [`string`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#string). +The drop-down menu UI component can be rendered in two ways: single-select, or multi-select. To render the dynamic input as a single-select menu, the input parameter defining the dynamic option must be of the type [`string`](/tools/deno-slack-sdk/reference/slack-types#string). ```js "step-with-dynamic-input": { @@ -133,7 +133,7 @@ The drop-down menu UI component can be rendered in two ways: single-select, or m } ``` -To render the dynamic input as a multi-select menu, the input parameter defining the dynamic option must be of the type [`array`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#array), and its `items` must be of type [`string`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#string). +To render the dynamic input as a multi-select menu, the input parameter defining the dynamic option must be of the type [`array`](/tools/deno-slack-sdk/reference/slack-types#array), and its `items` must be of type [`string`](/tools/deno-slack-sdk/reference/slack-types#string). ```js "step-with-dynamic-input": { @@ -159,9 +159,9 @@ To render the dynamic input as a multi-select menu, the input parameter defining #### Fields {#fields} -In the code snippet below, the input parameter is rendered as a set of fields with keys and values. The option fields are obtained from a custom step with an `options` output parameter of type [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field). +In the code snippet below, the input parameter is rendered as a set of fields with keys and values. The option fields are obtained from a custom step with an `options` output parameter of type [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field). -The input parameter that defines the dynamic option must be of type [`object`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#object), as the completed set of fields in Workflow Builder will be passed to the custom step as an [untyped object](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#untyped-object) during workflow execution. +The input parameter that defines the dynamic option must be of type [`object`](/tools/deno-slack-sdk/reference/slack-types#object), as the completed set of fields in Workflow Builder will be passed to the custom step as an [untyped object](/tools/deno-slack-sdk/reference/slack-types#untyped-object) during workflow execution. ```js "test-field-dynamic-options": { @@ -185,20 +185,20 @@ The input parameter that defines the dynamic option must be of type [`object`](h ### Dynamic option types {#dynamic-option-types} -As mentioned earlier, in order to use a custom step as a dynamic option, its output must adhere to a defined interface: it must have an `options` output parameter of the type either [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field). +As mentioned earlier, in order to use a custom step as a dynamic option, its output must adhere to a defined interface: it must have an `options` output parameter of the type either [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field). -To take a look at these in more detail, refer to our [Options Slack type](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options) documentation. +To take a look at these in more detail, refer to our [Options Slack type](/tools/deno-slack-sdk/reference/slack-types#options) documentation. ## Dynamic options handler {#dynamic-option-handler} Each custom step defined in the manifest needs a corresponding handler in your Slack app. Although implemented similarly to existing function execution event handlers, there are two key differences between regular custom step invocations and those used for dynamic options: -* The custom step must have an `options` output parameter that is of type [`options_select`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#options_field). -* The [`function_executed`](https://docs.slack.dev/reference/events/function_executed) event must be handled synchronously. This optimizes the response time of returned dynamic options and provides a crisp builder experience. +* The custom step must have an `options` output parameter that is of type [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field). +* The [`function_executed`](/reference/events/function_executed) event must be handled synchronously. This optimizes the response time of returned dynamic options and provides a crisp builder experience. ### Asynchronous event handling {#async} -By default, the [Bolt family of frameworks](https://tools.slack.dev/) handles `function_executed` events asynchronously. +By default, the Bolt family of frameworks handles `function_executed` events asynchronously. For example, the various modal-related API methods provide two ways to update a view: synchronously using a `response_action` HTTP response, or asynchronously using a separate HTTP API call. Using the asynchronous approach allows developers to handle events free of timeouts, but this isn't desired for dynamic options as it introduces delays and violates our stated goal of providing a crisp builder experience. @@ -208,13 +208,13 @@ Dynamic options support synchronous handling of `function_executed` events. By e ### Implementation {#implementation} -To optimize the response time of dynamic options, you must acknowledge the incoming event after calling the [`function.completeSuccess`](https://docs.slack.dev/reference/methods/functions.completeSuccess) or [`function.completeError`](https://docs.slack.dev/reference/methods/functions.completeError) API methods, minimizing asynchronous latency. The `function.completeSuccess` and `function.completeError` API methods are invoked in the complete and fail helper functions. ([For example](https://github.com/slackapi/bolt-python?tab=readme-ov-file#making-things-happen)). +To optimize the response time of dynamic options, you must acknowledge the incoming event after calling the [`function.completeSuccess`](/reference/methods/functions.completeSuccess) or [`function.completeError`](/reference/methods/functions.completeError) API methods, minimizing asynchronous latency. The `function.completeSuccess` and `function.completeError` API methods are invoked in the complete and fail helper functions. ([For example](https://github.com/slackapi/bolt-python?tab=readme-ov-file#making-things-happen)). A new `auto_acknowledge` flag allows you more granular control over whether specific event handlers should operate in synchronous or asynchronous response modes in order to enable a smooth dynamic options experience. #### Example {#bolt-py} -In [Bolt for Python](https://tools.slack.dev/bolt-python/), you can set `auto_acknowledge=False` on a specific function decorator. This allows you to manually control when the `ack()` event acknowledgement helper function is executed. It flips Bolt to synchronous `function_executed` event handling mode for the specific handler. +In [Bolt for Python](https://docs.slack.dev/tools/bolt-python/), you can set `auto_acknowledge=False` on a specific function decorator. This allows you to manually control when the `ack()` event acknowledgement helper function is executed. It flips Bolt to synchronous `function_executed` event handling mode for the specific handler. ```py @app.function("get-projects", auto_acknowledge=False) @@ -244,4 +244,4 @@ def handle_get_projects(ack: Ack, complete: Complete): ack() ``` -✨ **To learn more about the Bolt family of frameworks and tools**, check out our [Slack Developer Tools](https://tools.slack.dev/). +✨ **To learn more about the Bolt family of frameworks and tools**, check out our [Slack Developer Tools](/tools). diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/custom-steps.md b/docs/english/concepts/custom-steps.md similarity index 86% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/custom-steps.md rename to docs/english/concepts/custom-steps.md index e022c3e38..720c53421 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/custom-steps.md +++ b/docs/english/concepts/custom-steps.md @@ -1,17 +1,17 @@ --- -title: Listening and responding to custom steps -lang: ja-jp -slug: /concepts/custom-steps +sidebar_label: Custom steps --- -Your app can use the `function()` method to listen to incoming [custom step requests](https://docs.slack.dev/workflows/workflow-steps). Custom steps are used in Workflow Builder to build workflows. The method requires a step `callback_id` of type `str`. This `callback_id` must also be defined in your [Function](https://docs.slack.dev/reference/app-manifest#functions) definition. Custom steps must be finalized using the `complete()` or `fail()` listener arguments to notify Slack that your app has processed the request. +# Listening and responding to custom steps + +Your app can use the `function()` method to listen to incoming [custom step requests](/workflows/workflow-steps). Custom steps are used in Workflow Builder to build workflows. The method requires a step `callback_id` of type `str`. This `callback_id` must also be defined in your [Function](/reference/app-manifest#functions) definition. Custom steps must be finalized using the `complete()` or `fail()` listener arguments to notify Slack that your app has processed the request. * `complete()` requires **one** argument: `outputs` of type `dict`. It ends your custom step **successfully** and provides a dictionary containing the outputs of your custom step as per its definition. * `fail()` requires **one** argument: `error` of type `str`. It ends your custom step **unsuccessfully** and provides a message containing information regarding why your custom step failed. You can reference your custom step's inputs using the `inputs` listener argument of type `dict`. -Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn about the available listener arguments. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn about the available listener arguments. ```python # This sample custom step formats an input and outputs it @@ -68,13 +68,13 @@ Example app manifest definition Your app's custom steps may create interactivity points for users, for example: Post a message with a button. -If such interaction points originate from a custom step execution, the events sent to your app representing the end-user interaction with these points are considered to be _function-scoped interactivity events_. These interactivity events can be handled by your app using the same concepts we covered earlier, such as [Listening to actions](/concepts/action-listening). +If such interaction points originate from a custom step execution, the events sent to your app representing the end-user interaction with these points are considered to be _function-scoped interactivity events_. These interactivity events can be handled by your app using the same concepts we covered earlier, such as [Listening to actions](/tools/bolt-python/concepts/actions). _function-scoped interactivity events_ will contain data related to the custom step (`function_executed` event) they were spawned from, such as custom step `inputs` and access to `complete()` and `fail()` listener arguments. Your app can skip calling `complete()` or `fail()` in the `function()` handler method if the custom step creates an interaction point that requires user interaction before the step can end. However, in the relevant interactivity handler method, your app must invoke `complete()` or `fail()` to notify Slack that the custom step has been processed. -You’ll notice in all interactivity handler examples, `ack()` is used. It is required to call the `ack()` function within an interactivity listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests section](/concepts/acknowledge). +You’ll notice in all interactivity handler examples, `ack()` is used. It is required to call the `ack()` function within an interactivity listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests section](/tools/bolt-python/concepts/acknowledge). ```python # This sample custom step posts a message with a button @@ -150,4 +150,4 @@ Example app manifest definition -Learn more about responding to interactivity, see the [Slack API documentation](https://docs.slack.dev/interactivity/). +Learn more about responding to interactivity, see the [Slack API documentation](/interactivity/handling-user-interaction). diff --git a/docs/content/concepts/errors.md b/docs/english/concepts/errors.md similarity index 90% rename from docs/content/concepts/errors.md rename to docs/english/concepts/errors.md index d0e5cccad..ed41c5816 100644 --- a/docs/content/concepts/errors.md +++ b/docs/english/concepts/errors.md @@ -1,8 +1,4 @@ ---- -title: Handling errors -lang: en -slug: /concepts/errors ---- +# Handling errors 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`. diff --git a/docs/content/concepts/event-listening.md b/docs/english/concepts/event-listening.md similarity index 60% rename from docs/content/concepts/event-listening.md rename to docs/english/concepts/event-listening.md index 7ffa9e3a2..d7b8e5930 100644 --- a/docs/content/concepts/event-listening.md +++ b/docs/english/concepts/event-listening.md @@ -1,14 +1,10 @@ ---- -title: Listening to events -lang: en -slug: /concepts/event-listening ---- +# Listening to events -You can listen to [any Events API event](https://docs.slack.dev/reference/events) using the `event()` method after subscribing to it in your app configuration. This allows your app to take action when something happens in a workspace where it's installed, like a user reacting to a message or joining a channel. +You can listen to [any Events API event](/reference/events) using the `event()` method after subscribing to it in your app configuration. This allows your app to take action when something happens in a workspace where it's installed, like a user reacting to a message or joining a channel. The `event()` method requires an `eventType` of type `str`. -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # When a user joins the workspace, send a message in a predefined channel asking them to introduce themselves @app.event("team_join") @@ -23,7 +19,7 @@ def ask_for_introduction(event, say): The `message()` listener is equivalent to `event("message")`. -You can filter on subtypes of events by passing in the additional key `subtype`. Common message subtypes like `bot_message` and `message_replied` can be found [on the message event page](https://docs.slack.dev/reference/events/message#subtypes). +You can filter on subtypes of events by passing in the additional key `subtype`. Common message subtypes like `bot_message` and `message_replied` can be found [on the message event page](/reference/events/message#subtypes). You can explicitly filter for events without a subtype by explicitly setting `None`. ```python diff --git a/docs/content/concepts/global-middleware.md b/docs/english/concepts/global-middleware.md similarity index 82% rename from docs/content/concepts/global-middleware.md rename to docs/english/concepts/global-middleware.md index ec748c000..dbcdeae99 100644 --- a/docs/content/concepts/global-middleware.md +++ b/docs/english/concepts/global-middleware.md @@ -1,14 +1,10 @@ ---- -title: Global middleware -lang: en -slug: /concepts/global-middleware ---- +# Global middleware Global middleware is run for all incoming requests, before any listener middleware. You can add any number of global middleware to your app by passing middleware functions to `app.use()`. Middleware functions are called with the same arguments as listeners, with an additional `next()` function. Both global and listener middleware must call `next()` to pass control of the execution chain to the next middleware. -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python @app.use def auth_acme(client, context, logger, payload, next): diff --git a/docs/content/concepts/lazy-listeners.md b/docs/english/concepts/lazy-listeners.md similarity index 98% rename from docs/content/concepts/lazy-listeners.md rename to docs/english/concepts/lazy-listeners.md index d72c2f9c0..d775106b9 100644 --- a/docs/content/concepts/lazy-listeners.md +++ b/docs/english/concepts/lazy-listeners.md @@ -1,8 +1,4 @@ ---- -title: Lazy listeners (FaaS) -lang: en -slug: /concepts/lazy-listeners ---- +# Lazy listeners (FaaS) Lazy Listeners are a feature which make it easier to deploy Slack apps to FaaS (Function-as-a-Service) environments. Please note that this feature is only available in Bolt for Python, and we are not planning to add the same to other Bolt frameworks. diff --git a/docs/content/concepts/listener-middleware.md b/docs/english/concepts/listener-middleware.md similarity index 83% rename from docs/content/concepts/listener-middleware.md rename to docs/english/concepts/listener-middleware.md index 3507d7d97..c8bfc964e 100644 --- a/docs/content/concepts/listener-middleware.md +++ b/docs/english/concepts/listener-middleware.md @@ -1,14 +1,10 @@ ---- -title: Listener middleware -lang: en -slug: /concepts/listener-middleware ---- +# Listener middleware Listener middleware is only run for the listener in which it's passed. You can pass any number of middleware functions to the listener using the `middleware` parameter, which must be a list that contains one to many middleware functions. If your listener middleware is a quite simple one, you can use a listener matcher, which returns `bool` value (`True` for proceeding) instead of requiring `next()` method call. -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Listener middleware which filters out messages from a bot diff --git a/docs/content/concepts/logging.md b/docs/english/concepts/logging.md similarity index 94% rename from docs/content/concepts/logging.md rename to docs/english/concepts/logging.md index 5f82d168a..49e275d2d 100644 --- a/docs/content/concepts/logging.md +++ b/docs/english/concepts/logging.md @@ -1,8 +1,4 @@ ---- -title: Logging -lang: en -slug: /concepts/logging ---- +# Logging 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`. diff --git a/docs/content/concepts/message-listening.md b/docs/english/concepts/message-listening.md similarity index 59% rename from docs/content/concepts/message-listening.md rename to docs/english/concepts/message-listening.md index b2f8bc05c..be6e74678 100644 --- a/docs/content/concepts/message-listening.md +++ b/docs/english/concepts/message-listening.md @@ -1,16 +1,9 @@ ---- -title: Listening to messages -lang: en -slug: /concepts/message-listening ---- - -To listen to messages that [your app has access to receive](https://docs.slack.dev/messaging/retrieving-messages), you can use the `message()` method which filters out events that aren't of type `message`. +# Listening to messages +To listen to messages that [your app has access to receive](/messaging/retrieving-messages), you can use the `message()` method which filters out events that aren't of type `message`. `message()` accepts an argument of type `str` or `re.Pattern` object that filters out any messages that don't match the pattern. -:::info - -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. +:::info[Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments.] ::: diff --git a/docs/content/concepts/message-sending.md b/docs/english/concepts/message-sending.md similarity index 77% rename from docs/content/concepts/message-sending.md rename to docs/english/concepts/message-sending.md index b4a2f309e..228a7b6b8 100644 --- a/docs/content/concepts/message-sending.md +++ b/docs/english/concepts/message-sending.md @@ -1,14 +1,10 @@ ---- -title: Sending messages -lang: en -slug: /concepts/message-sending ---- +# Sending messages Within your listener function, `say()` is available whenever there is an associated conversation (for example, a conversation where the event or action which triggered the listener occurred). `say()` accepts a string to post simple messages and JSON payloads to send more complex messages. The message payload you pass in will be sent to the associated conversation. -In the case that you'd like to send a message outside of a listener or you want to do something more advanced (like handle specific errors), you can call `client.chat_postMessage` [using the client attached to your Bolt instance](/concepts/web-api). +In the case that you'd like to send a message outside of a listener or you want to do something more advanced (like handle specific errors), you can call `client.chat_postMessage` [using the client attached to your Bolt instance](/tools/bolt-python/concepts/web-api). -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Listens for messages containing "knock knock" and responds with an italicized "who's there?" @app.message("knock knock") @@ -20,7 +16,7 @@ def ask_who(message, say): `say()` accepts more complex message payloads to make it easy to add functionality and structure to your messages. -To explore adding rich message layouts to your app, read through [the guide on our API site](https://docs.slack.dev/messaging/#structure) and look through templates of common app flows [in the Block Kit Builder](https://api.slack.com/tools/block-kit-builder?template=1). +To explore adding rich message layouts to your app, read through [the guide on our API site](/messaging/#structure) and look through templates of common app flows [in the Block Kit Builder](https://api.slack.com/tools/block-kit-builder?template=1). ```python # Sends a section block with datepicker when someone reacts with a 📅 emoji diff --git a/docs/content/concepts/opening-modals.md b/docs/english/concepts/opening-modals.md similarity index 68% rename from docs/content/concepts/opening-modals.md rename to docs/english/concepts/opening-modals.md index e7daceafc..1f053539f 100644 --- a/docs/content/concepts/opening-modals.md +++ b/docs/english/concepts/opening-modals.md @@ -1,16 +1,12 @@ ---- -title: Opening modals -lang: en -slug: /concepts/opening-modals ---- +# Opening modals -[Modals](https://docs.slack.dev/surfaces/modals) are focused surfaces that allow you to collect user data and display dynamic information. You can open a modal by passing a valid `trigger_id` and a [view payload](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission) to the built-in client's [`views.open`](https://docs.slack.dev/reference/methods/views.open/) method. +[Modals](/surfaces/modals) are focused surfaces that allow you to collect user data and display dynamic information. You can open a modal by passing a valid `trigger_id` and a [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission) to the built-in client's [`views.open`](/reference/methods/views.open/) method. Your app receives `trigger_id` parameters in payloads sent to your Request URL triggered user invocation like a slash command, button press, or interaction with a select menu. -Read more about modal composition in the [API documentation](https://docs.slack.dev/surfaces/modals#composing_views). +Read more about modal composition in the [API documentation](/surfaces/modals#composing_views). -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Listen for a shortcut invocation diff --git a/docs/content/concepts/select-menu-options.md b/docs/english/concepts/select-menu-options.md similarity index 67% rename from docs/content/concepts/select-menu-options.md rename to docs/english/concepts/select-menu-options.md index d699e6370..40d29472c 100644 --- a/docs/content/concepts/select-menu-options.md +++ b/docs/english/concepts/select-menu-options.md @@ -1,19 +1,15 @@ ---- -title: Listening & responding to select menu options -lang: en -slug: /concepts/options ---- +# Listening & responding to select menu options -The `options()` method listens for incoming option request payloads from Slack. [Similar to `action()`](/concepts/action-listening), +The `options()` method listens for incoming option request payloads from Slack. [Similar to `action()`](/tools/bolt-python/concepts/actions), an `action_id` or constraints object is required. In order to load external data into your select menus, you must provide an options load URL in your app configuration, appended with `/slack/events`. While it's recommended to use `action_id` for `external_select` menus, dialogs do not support Block Kit so you'll have to use the constraints object to filter on a `callback_id`. -To respond to options requests, you'll need to call `ack()` with a valid `options` or `option_groups` list. Both [external select response examples](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select) and [dialog response examples](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) can be found on our API site. +To respond to options requests, you'll need to call `ack()` with a valid `options` or `option_groups` list. Both [external select response examples](/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select) and [dialog response examples](/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) can be found on our API site. -Additionally, you may want to apply filtering logic to the returned options based on user input. This can be accomplished by using the `payload` argument to your options listener and checking for the contents of the `value` property within it. Based on the `value` you can return different options. All listeners and middleware handlers in Bolt for Python have access to [many useful arguments](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) - be sure to check them out! +Additionally, you may want to apply filtering logic to the returned options based on user input. This can be accomplished by using the `payload` argument to your options listener and checking for the contents of the `value` property within it. Based on the `value` you can return different options. All listeners and middleware handlers in Bolt for Python have access to [many useful arguments](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) - be sure to check them out! -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Example of responding to an external_select options request @app.options("external_action") diff --git a/docs/content/concepts/shortcuts.md b/docs/english/concepts/shortcuts.md similarity index 74% rename from docs/content/concepts/shortcuts.md rename to docs/english/concepts/shortcuts.md index 6833d468d..b28f0b352 100644 --- a/docs/content/concepts/shortcuts.md +++ b/docs/english/concepts/shortcuts.md @@ -1,22 +1,18 @@ ---- -title: Listening & responding to shortcuts -lang: en -slug: /concepts/shortcuts ---- +# Listening & responding to shortcuts -The `shortcut()` method supports both [global shortcuts](https://docs.slack.dev/interactivity/implementing-shortcuts#global) and [message shortcuts](https://docs.slack.dev/interactivity/implementing-shortcuts#messages). +The `shortcut()` method supports both [global shortcuts](/interactivity/implementing-shortcuts#global) and [message shortcuts](/interactivity/implementing-shortcuts#messages). Shortcuts are invokable entry points to apps. Global shortcuts are available from within search and text composer area in Slack. Message shortcuts are available in the context menus of messages. Your app can use the `shortcut()` method to listen to incoming shortcut requests. The method requires a `callback_id` parameter of type `str` or `re.Pattern`. Shortcuts must be acknowledged with `ack()` to inform Slack that your app has received the request. -Shortcuts include a `trigger_id` which an app can use to [open a modal](/concepts/opening-modals) that confirms the action the user is taking. +Shortcuts include a `trigger_id` which an app can use to [open a modal](/tools/bolt-python/concepts/opening-modals) that confirms the action the user is taking. When setting up shortcuts within your app configuration, as with other URLs, you'll append `/slack/events` to your request URL. -⚠️ Note that global shortcuts do **not** include a channel ID. If your app needs access to a channel ID, you may use a [`conversations_select`](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) element within a modal. Message shortcuts do include a channel ID. +⚠️ Note that global shortcuts do **not** include a channel ID. If your app needs access to a channel ID, you may use a [`conversations_select`](/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) element within a modal. Message shortcuts do include a channel ID. -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # The open_modal shortcut listens to a shortcut with the callback_id "open_modal" @app.shortcut("open_modal") @@ -36,7 +32,7 @@ def open_modal(ack, shortcut, client): "type": "section", "text": { "type": "mrkdwn", - "text": "About the simplest modal you could conceive of :smile:\n\nMaybe or ." + "text": "About the simplest modal you could conceive of :smile:\n\nMaybe or ." } }, { @@ -75,7 +71,7 @@ def open_modal(ack, shortcut, client): "type": "section", "text": { "type": "mrkdwn", - "text": "About the simplest modal you could conceive of :smile:\n\nMaybe or ." + "text": "About the simplest modal you could conceive of :smile:\n\nMaybe or ." } }, { diff --git a/docs/content/concepts/socket-mode.md b/docs/english/concepts/socket-mode.md similarity index 78% rename from docs/content/concepts/socket-mode.md rename to docs/english/concepts/socket-mode.md index 301fbf94e..5156f6e13 100644 --- a/docs/content/concepts/socket-mode.md +++ b/docs/english/concepts/socket-mode.md @@ -1,10 +1,6 @@ ---- -title: Using Socket Mode -lang: en -slug: /concepts/socket-mode ---- +# Using Socket Mode -With the introduction of [Socket Mode](https://docs.slack.dev/apis/events-api/using-socket-mode), Bolt for Python introduced support in version `1.2.0`. With Socket Mode, instead of creating a server with endpoints that Slack sends payloads too, the app will instead connect to Slack via a WebSocket connection and receive data from Slack over the socket connection. Make sure to enable Socket Mode in your app configuration settings. +With the introduction of [Socket Mode](/apis/events-api/using-socket-mode), Bolt for Python introduced support in version `1.2.0`. With Socket Mode, instead of creating a server with endpoints that Slack sends payloads too, the app will instead connect to Slack via a WebSocket connection and receive data from Slack over the socket connection. Make sure to enable Socket Mode in your app configuration settings. To use the Socket Mode, add `SLACK_APP_TOKEN` as an environment variable. You can get your App Token in your app configuration settings under the **Basic Information** section. @@ -38,7 +34,7 @@ if __name__ == "__main__": To use the asyncio-based adapters such as aiohttp, your whole app needs to be compatible with asyncio's async/await programming model. `AsyncSocketModeHandler` is available for running `AsyncApp` and its async middleware and listeners. -To learn how to use `AsyncApp`, checkout the [using Async](/concepts/async) document and relevant [examples](https://github.com/slackapi/bolt-python/tree/main/examples). +To learn how to use `AsyncApp`, checkout the [using Async](/tools/bolt-python/concepts/async) document and relevant [examples](https://github.com/slackapi/bolt-python/tree/main/examples). ```python from slack_bolt.app.async_app import AsyncApp diff --git a/docs/content/concepts/token-rotation.md b/docs/english/concepts/token-rotation.md similarity index 73% rename from docs/content/concepts/token-rotation.md rename to docs/english/concepts/token-rotation.md index 88af29ffa..96a41bb3c 100644 --- a/docs/content/concepts/token-rotation.md +++ b/docs/english/concepts/token-rotation.md @@ -1,13 +1,9 @@ ---- -title: Token rotation -lang: en -slug: /concepts/token-rotation ---- +# Token rotation 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). 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. -Bolt for Python supports and will handle token rotation automatically so long as the [built-in OAuth](/concepts/authenticating-oauth) functionality is used. +Bolt for Python supports and will handle token rotation automatically so long as the [built-in OAuth](/tools/bolt-python/concepts/authenticating-oauth) functionality is used. -For more information about token rotation, please see the [documentation](https://docs.slack.dev/authentication/using-token-rotation). \ No newline at end of file +For more information about token rotation, please see the [documentation](/authentication/using-token-rotation). \ No newline at end of file diff --git a/docs/content/concepts/updating-pushing-views.md b/docs/english/concepts/updating-pushing-views.md similarity index 64% rename from docs/content/concepts/updating-pushing-views.md rename to docs/english/concepts/updating-pushing-views.md index aa1efa3fa..8c05e79c8 100644 --- a/docs/content/concepts/updating-pushing-views.md +++ b/docs/english/concepts/updating-pushing-views.md @@ -1,10 +1,6 @@ ---- -title: Updating & pushing views -lang: en -slug: /concepts/updating-pushing-views ---- +# Updating & pushing views -Modals contain a stack of views. When you call [`views_open`](https://api.https://docs.slack.dev/reference/methods/views.open/slack.com/methods/views.open), you add the root view to the modal. After the initial call, you can dynamically update a view by calling [`views_update`](https://docs.slack.dev/reference/methods/views.update/), or stack a new view on top of the root view by calling [`views_push`](https://docs.slack.dev/reference/methods/views.push/) +Modals contain a stack of views. When you call [`views_open`](https://api./reference/methods/views.open/slack.com/methods/views.open), you add the root view to the modal. After the initial call, you can dynamically update a view by calling [`views_update`](/reference/methods/views.update/), or stack a new view on top of the root view by calling [`views_push`](/reference/methods/views.push/) ## The `views_update` method @@ -12,11 +8,11 @@ To update a view, you can use the built-in client to call `views_update` with th ## The `views_push` method -To push a new view onto the view stack, you can use the built-in client to call `views_push` with a valid `trigger_id` a new [view payload](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission). The arguments for `views_push` is the same as [opening modals](/concepts/creating-models). After you open a modal, you may only push two additional views onto the view stack. +To push a new view onto the view stack, you can use the built-in client to call `views_push` with a valid `trigger_id` a new [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission). The arguments for `views_push` is the same as [opening modals](/tools/bolt-python/concepts/opening-modals). After you open a modal, you may only push two additional views onto the view stack. -Learn more about updating and pushing views in our [API documentation](https://docs.slack.dev/surfaces/modals) +Learn more about updating and pushing views in our [API documentation](/surfaces/modals) -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Listen for a button invocation with action_id `button_abc` (assume it's inside of a modal) @app.action("button_abc") diff --git a/docs/content/concepts/view-submissions.md b/docs/english/concepts/view-submissions.md similarity index 74% rename from docs/content/concepts/view-submissions.md rename to docs/english/concepts/view-submissions.md index 60b78cd54..4ff4c2da7 100644 --- a/docs/content/concepts/view-submissions.md +++ b/docs/english/concepts/view-submissions.md @@ -1,10 +1,6 @@ ---- -title: Listening to views -lang: en -slug: /concepts/view_submissions ---- +# Listening to views -If a [view payload](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission) contains any input blocks, you must listen to `view_submission` requests to receive their values. To listen to `view_submission` requests, you can use the built-in `view()` method. `view()` requires a `callback_id` of type `str` or `re.Pattern`. +If a [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission) contains any input blocks, you must listen to `view_submission` requests to receive their values. To listen to `view_submission` requests, you can use the built-in `view()` method. `view()` requires a `callback_id` of type `str` or `re.Pattern`. You can access the value of the `input` blocks by accessing the `state` object. `state` contains a `values` object that uses the `block_id` and unique `action_id` to store the input values. @@ -23,9 +19,9 @@ def handle_submission(ack, body): # https://app.slack.com/block-kit-builder/#%7B%22type%22:%22modal%22,%22callback_id%22:%22view_1%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22My%20App%22,%22emoji%22:true%7D,%22blocks%22:%5B%5D%7D ack(response_action="update", view=build_new_view(body)) ``` -Similarly, there are options for [displaying errors](https://docs.slack.dev/surfaces/modals#displaying_errors) in response to view submissions. +Similarly, there are options for [displaying errors](/surfaces/modals#displaying_errors) in response to view submissions. -Read more about view submissions in our [API documentation](https://docs.slack.dev/surfaces/modals#interactions) +Read more about view submissions in our [API documentation](/surfaces/modals#interactions) --- @@ -33,7 +29,7 @@ Read more about view submissions in our [API documentation](https://docs.slack.d When listening for `view_closed` requests, you must pass `callback_id` and add a `notify_on_close` property to the view during creation. See below for an example of this: -See the [API documentation](https://docs.slack.dev/surfaces/modals#interactions) for more information about `view_closed`. +See the [API documentation](/surfaces/modals#interactions) for more information about `view_closed`. ```python @@ -62,7 +58,7 @@ def handle_view_closed(ack, body, logger): logger.info(body) ``` -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. +Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ```python # Handle a view_submission request @app.view("view_1") diff --git a/docs/english/concepts/web-api.md b/docs/english/concepts/web-api.md new file mode 100644 index 000000000..9cf436851 --- /dev/null +++ b/docs/english/concepts/web-api.md @@ -0,0 +1,22 @@ +# Using the Web API + +You can call [any Web API method](/reference/methods) using the `WebClient` 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. + +The token used to initialize Bolt can be found in the `context` object, which is required to call most Web API methods. + +:::info[Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments.] + +::: + +```python +@app.message("wake me up") +def say_hello(client, message): + # Unix Epoch time for September 30, 2020 11:59:59 PM + when_september_ends = 1601510399 + channel_id = message["channel"] + client.chat_scheduleMessage( + channel=channel_id, + post_at=when_september_ends, + text="Summer has come and passed" + ) +``` diff --git a/docs/content/getting-started.md b/docs/english/getting-started.md similarity index 80% rename from docs/content/getting-started.md rename to docs/english/getting-started.md index a794f3176..ebdf47189 100644 --- a/docs/content/getting-started.md +++ b/docs/english/getting-started.md @@ -1,9 +1,8 @@ --- -title: Quickstart guide with Bolt for Python sidebar_label: Quickstart --- -# Getting started with Bolt for Python +# Quickstart guide with Bolt for Python This quickstart guide aims to help you get a Slack app using Bolt for Python up and running as soon as possible! @@ -14,20 +13,20 @@ When complete, you'll have a local environment configured with a customized [app :::tip[Reference for readers] -In search of the complete guide to building an app from scratch? Check out the [building an app](/building-an-app) guide. +In search of the complete guide to building an app from scratch? Check out the [building an app](/tools/bolt-python/building-an-app) guide. ::: #### Prerequisites -A few tools are needed for the following steps. We recommend using the [**Slack CLI**](https://tools.slack.dev/slack-cli/) for the smoothest experience, but other options remain available. +A few tools are needed for the following steps. We recommend using the [**Slack CLI**](/tools/slack-cli/) for the smoothest experience, but other options remain available. You can also begin by installing git and downloading [Python 3.6 or later](https://www.python.org/downloads/), or the latest stable version of Python. Refer to [Python's setup and building guide](https://devguide.python.org/getting-started/setup-building/) for more details. Install the latest version of the Slack CLI to get started: -- [Slack CLI for macOS & Linux](https://tools.slack.dev/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux) -- [Slack CLI for Windows](https://tools.slack.dev/slack-cli/guides/installing-the-slack-cli-for-windows) +- [Slack CLI for macOS & Linux](/tools/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux) +- [Slack CLI for Windows](/tools/slack-cli/guides/installing-the-slack-cli-for-windows) Then confirm a successful installation with the following command: @@ -45,7 +44,7 @@ $ slack login A workspace where development can happen is also needed. -We recommend using [developer sandboxes](https://docs.slack.dev/tools/developer-sandboxes) to avoid disruptions where real work gets done. +We recommend using [developer sandboxes](/tools/developer-sandboxes) to avoid disruptions where real work gets done. ::: @@ -133,9 +132,9 @@ Navigate to your list of apps and [create a new Slack app](https://api.slack.com You'll then land on your app's **Basic Information** page, which is an overview of your app and which contains important credentials: -![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") +![Basic Information page](/img/bolt-python/basic-information-page.png "Basic Information page") -To listen for events happening in Slack (such as a new posted message) without opening a port or exposing an endpoint, we will use [Socket Mode](/concepts/socket-mode). This connection requires a specific app token: +To listen for events happening in Slack (such as a new posted message) without opening a port or exposing an endpoint, we will use [Socket Mode](/tools/bolt-python/concepts/socket-mode). This connection requires a specific app token: 1. On the **Basic Information** page, scroll to the **App-Level Tokens** section and click **Generate Token and Scopes**. 2. Name the token "Development" or something similar and add the `connections:write` scope, then click **Generate**. @@ -149,7 +148,7 @@ The above command works on Linux and macOS but [similar commands are available o :::warning[Keep it secret. Keep it safe.] -Treat your tokens like a password and [keep it safe](https://docs.slack.dev/authentication/best-practices-for-security). Your app uses these to retrieve and send information to Slack. +Treat your tokens like a password and [keep it safe](/authentication/best-practices-for-security). Your app uses these to retrieve and send information to Slack. ::: @@ -158,7 +157,7 @@ A bot token is also needed to interact with the Web API methods as your app's bo 1. Navigate to the **OAuth & Permissions** on the left sidebar and install your app to your workspace to generate a token. 2. After authorizing the installation, you'll return to the **OAuth & Permissions** page and find a **Bot User OAuth Token**: -![OAuth Tokens](/img/boltpy/bot-token.png "Bot OAuth Token") +![OAuth Tokens](/img/bolt-python/bot-token.png "Bot OAuth Token") 3. Copy the bot token beginning with `xoxb` from the **OAuth & Permissions page** and then store it in a new environment variable: @@ -252,7 +251,7 @@ Your app can be stopped again by pressing `CTRL+C` in the terminal to end these #### Customizing app settings -The created app will have some placeholder values and a small set of [scopes](https://docs.slack.dev/reference/scopes) to start, but we recommend exploring the customizations possible on app settings. +The created app will have some placeholder values and a small set of [scopes](/reference/scopes) to start, but we recommend exploring the customizations possible on app settings. @@ -265,7 +264,7 @@ $ slack app settings This will open the following page in a web browser: -![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") +![Basic Information page](/img/bolt-python/basic-information-page.png "Basic Information page") @@ -274,7 +273,7 @@ Browse to https://api.slack.com/apps and select your app "Getting Started Bolt A This will open the following page: -![Basic Information page](/img/boltpy/basic-information-page.png "Basic Information page") +![Basic Information page](/img/bolt-python/basic-information-page.png "Basic Information page") @@ -287,14 +286,14 @@ Congrats once more on getting up and running with this quick start. :::info[Dive deeper] -Follow along with the steps that went into making this app on the [building an app](/building-an-app) guide for an educational overview. +Follow along with the steps that went into making this app on the [building an app](/tools/bolt-python/building-an-app) guide for an educational overview. ::: You can now continue customizing your app with various features to make it right for whatever job's at hand. Here are some ideas about what to explore next: -- Explore the different events your bot can listen to with the [`app.event()`](/concepts/event-listening) method. All of the [events](https://docs.slack.dev/reference/events) are listed on the API docs site. -- Bolt allows you to call [Web API](/concepts/web-api) methods with the client attached to your app. There are [over 200 methods](https://docs.slack.dev/reference/methods) on the API docs site. -- Learn more about the different [token types](https://docs.slack.dev/authentication/tokens) and [authentication setups](/concepts/authenticating-oauth). Your app might need different tokens depending on the actions you want to perform or for installations to multiple workspaces. -- Receive events using HTTP for various deployment methods, such as deploying to [Heroku](/deployments/heroku) or [AWS Lambda](/deployments/aws-lambda). -- Read on [app design](https://docs.slack.dev/surfaces/app-design) and compose fancy messages with blocks using [Block Kit Builder](https://app.slack.com/block-kit-builder) to prototype messages. +- Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. All of the [events](/reference/events) are listed on the API docs site. +- Bolt allows you to call [Web API](/tools/bolt-python/concepts/web-api) methods with the client attached to your app. There are [over 200 methods](/reference/methods) on the API docs site. +- Learn more about the different [token types](/authentication/tokens) and [authentication setups](/tools/bolt-python/concepts/authenticating-oauth). Your app might need different tokens depending on the actions you want to perform or for installations to multiple workspaces. +- Receive events using HTTP for various deployment methods, such as deploying to Heroku or AWS Lambda. +- Read on [app design](/surfaces/app-design) and compose fancy messages with blocks using [Block Kit Builder](https://app.slack.com/block-kit-builder) to prototype messages. diff --git a/docs/content/index.md b/docs/english/index.md similarity index 93% rename from docs/content/index.md rename to docs/english/index.md index 33204bca1..212bd9690 100644 --- a/docs/content/index.md +++ b/docs/english/index.md @@ -1,6 +1,6 @@ # Bolt for Python -Bolt for Python is a Python framework to build Slack apps with the latest Slack platform features. Read the [Getting Started Guide](/getting-started) to set up and run your first Bolt app. +Bolt for Python is a Python framework to build Slack apps with the latest Slack platform features. Read the [Getting Started Guide](/tools/bolt-python/getting-started) to set up and run your first Bolt app. Then, explore the rest of the pages within the Guides section. The documentation there will help you build a Bolt app for whatever use case you may have. diff --git a/docs/content/concepts/steps-from-apps.md b/docs/english/legacy/steps-from-apps.md similarity index 67% rename from docs/content/concepts/steps-from-apps.md rename to docs/english/legacy/steps-from-apps.md index 09becda0c..03b9fa8ff 100644 --- a/docs/content/concepts/steps-from-apps.md +++ b/docs/english/legacy/steps-from-apps.md @@ -1,20 +1,14 @@ ---- -title: Steps from apps -lang: en -slug: /legacy/steps-from-apps ---- +# Steps from apps -:::danger +:::danger[Steps from Apps is a deprecated feature.] -Steps from Apps is a deprecated feature. +Steps from Apps are different than, and not interchangeable with, Slack automation workflows. We encourage those who are currently publishing steps from apps to consider the new [Slack automation features](/workflows/), such as [custom steps for Bolt](/workflows/workflow-steps). -Steps from Apps are different than, and not interchangeable with, Slack automation workflows. We encourage those who are currently publishing steps from apps to consider the new [Slack automation features](https://docs.slack.dev/workflows/), such as [custom steps for Bolt](https://docs.slack.dev/workflows/workflow-steps). - -Please [read the Slack API changelog entry](https://docs.slack.dev/changelog/2023-08-workflow-steps-from-apps-step-back) for more information. +Please [read the Slack API changelog entry](/changelog/2023-08-workflow-steps-from-apps-step-back) for more information. ::: -Steps from apps allow your app to create and process steps that users can add using [Workflow Builder](https://docs.slack.dev/workflows/workflow-builder). +Steps from apps allow your app to create and process steps that users can add using [Workflow Builder](/workflows/workflow-builder). Steps from apps are made up of three distinct user events: @@ -24,7 +18,7 @@ Steps from apps are made up of three distinct user events: All three events must be handled for a step from app to function. -Read more about steps from apps in the [API documentation](https://docs.slack.dev/workflows/workflow-steps). +Read more about steps from apps in the [API documentation](/workflows/workflow-steps). ## Creating steps from apps @@ -36,9 +30,9 @@ The configuration object contains three keys: `edit`, `save`, and `execute`. Eac After instantiating a `WorkflowStep`, you can pass it into `app.step()`. Behind the scenes, your app will listen and respond to the step’s events using the callbacks provided in the configuration object. -Alternatively, steps from apps can also be created using the `WorkflowStepBuilder` class alongside a decorator pattern. For more information, including an example of this approach, [refer to the documentation](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder). +Alternatively, steps from apps can also be created using the `WorkflowStepBuilder` class alongside a decorator pattern. For more information, including an example of this approach, [refer to the documentation](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder). -Refer to the module documents ([common](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) / [step-specific](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/utilities/index.html)) to learn the available arguments. +Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ```python import os @@ -74,15 +68,15 @@ app.step(ws) ## Adding or editing steps from apps -When a builder adds (or later edits) your step in their workflow, your app will receive a [`workflow_step_edit` event](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step_edit-payload). The `edit` callback in your `WorkflowStep` configuration will be run when this event is received. +When a builder adds (or later edits) your step in their workflow, your app will receive a [`workflow_step_edit` event](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step_edit-payload). The `edit` callback in your `WorkflowStep` configuration will be run when this event is received. -Whether a builder is adding or editing a step, you need to send them a [step from app configuration modal](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object). This modal is where step-specific settings are chosen, and it has more restrictions than typical modals—most notably, it cannot include `title`, `submit`, or `close` properties. By default, the configuration modal's `callback_id` will be the same as the step from app. +Whether a builder is adding or editing a step, you need to send them a [step from app configuration modal](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object). This modal is where step-specific settings are chosen, and it has more restrictions than typical modals—most notably, it cannot include `title`, `submit`, or `close` properties. By default, the configuration modal's `callback_id` will be the same as the step from app. Within the `edit` callback, the `configure()` utility can be used to easily open your step's configuration modal by passing in the view's blocks with the corresponding `blocks` argument. To disable saving the configuration before certain conditions are met, you can also pass in `submit_disabled` with a value of `True`. -To learn more about opening configuration modals, [read the documentation](https://docs.slack.dev/legacy/legacy-steps-from-apps/). +To learn more about opening configuration modals, [read the documentation](/legacy/legacy-steps-from-apps/). -Refer to the module documents ([common](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) / [step-specific](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/utilities/index.html)) to learn the available arguments. +Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ```python def edit(ack, step, configure): @@ -132,9 +126,9 @@ Within the `save` callback, the `update()` method can be used to save the builde - `step_name` overrides the default Step name - `step_image_url` overrides the default Step image -To learn more about how to structure these parameters, [read the documentation](https://docs.slack.dev/legacy/legacy-steps-from-apps/). +To learn more about how to structure these parameters, [read the documentation](/legacy/legacy-steps-from-apps/). -Refer to the module documents ([common](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) / [step-specific](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/utilities/index.html)) to learn the available arguments. +Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ```python def save(ack, view, update): @@ -173,13 +167,13 @@ app.step(ws) ## Executing steps from apps -When your step from app is executed by an end user, your app will receive a [`workflow_step_execute` event](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object). The `execute` callback in your `WorkflowStep` configuration will be run when this event is received. +When your step from app is executed by an end user, your app will receive a [`workflow_step_execute` event](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object). The `execute` callback in your `WorkflowStep` configuration will be run when this event is received. Using the `inputs` from the `save` callback, this is where you can make third-party API calls, save information to a database, update the user's Home tab, or decide the outputs that will be available to subsequent steps from apps by mapping values to the `outputs` object. Within the `execute` callback, your app must either call `complete()` to indicate that the step's execution was successful, or `fail()` to indicate that the step's execution failed. -Refer to the module documents ([common](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) / [step-specific](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/utilities/index.html)) to learn the available arguments. +Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ```python def execute(step, complete, fail): diff --git a/docs/static/img/tutorials/ai-chatbot/1.png b/docs/english/tutorial/ai-chatbot/1.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/1.png rename to docs/english/tutorial/ai-chatbot/1.png diff --git a/docs/static/img/tutorials/ai-chatbot/2.png b/docs/english/tutorial/ai-chatbot/2.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/2.png rename to docs/english/tutorial/ai-chatbot/2.png diff --git a/docs/static/img/tutorials/ai-chatbot/3.png b/docs/english/tutorial/ai-chatbot/3.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/3.png rename to docs/english/tutorial/ai-chatbot/3.png diff --git a/docs/static/img/tutorials/ai-chatbot/4.png b/docs/english/tutorial/ai-chatbot/4.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/4.png rename to docs/english/tutorial/ai-chatbot/4.png diff --git a/docs/static/img/tutorials/ai-chatbot/5.png b/docs/english/tutorial/ai-chatbot/5.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/5.png rename to docs/english/tutorial/ai-chatbot/5.png diff --git a/docs/static/img/tutorials/ai-chatbot/6.png b/docs/english/tutorial/ai-chatbot/6.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/6.png rename to docs/english/tutorial/ai-chatbot/6.png diff --git a/docs/static/img/tutorials/ai-chatbot/7.png b/docs/english/tutorial/ai-chatbot/7.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/7.png rename to docs/english/tutorial/ai-chatbot/7.png diff --git a/docs/static/img/tutorials/ai-chatbot/8.png b/docs/english/tutorial/ai-chatbot/8.png similarity index 100% rename from docs/static/img/tutorials/ai-chatbot/8.png rename to docs/english/tutorial/ai-chatbot/8.png diff --git a/docs/content/tutorial/ai-chatbot.md b/docs/english/tutorial/ai-chatbot/ai-chatbot.md similarity index 88% rename from docs/content/tutorial/ai-chatbot.md rename to docs/english/tutorial/ai-chatbot/ai-chatbot.md index 7db10b722..fa4da90a7 100644 --- a/docs/content/tutorial/ai-chatbot.md +++ b/docs/english/tutorial/ai-chatbot/ai-chatbot.md @@ -32,7 +32,7 @@ If you'd rather skip the tutorial and just head straight to the code, you can us Before you'll be able to successfully run the app, you'll need to first obtain and set some environment variables. 1. On the **Install App** page, copy your **Bot User OAuth Token**. You will store this in your environment as `SLACK_BOT_TOKEN` (we'll get to that next). -2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](https://docs.slack.dev/reference/scopes/connections.write) scope, name the token, and click **Generate**. (For more details, refer to [understanding OAuth scopes for bots](https://docs.slack.dev/authentication/tokens#bot)). Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`. +2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](/reference/scopes/connections.write) scope, name the token, and click **Generate**. (For more details, refer to [understanding OAuth scopes for bots](/authentication/tokens#bot)). Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`. To store your tokens and environment variables, run the following commands in the terminal. Replace the placeholder values with your bot and app tokens collected above, as well as the key or keys for the AI provider or providers you want to use: @@ -100,7 +100,7 @@ Navigate to the Bolty **App Home** and select a provider from the drop-down menu If you don't see Bolty listed under **Apps** in your workspace right away, never fear! You can mention **@Bolty** in a public channel to add the app, then navigate to your **App Home**. -![Choose your AI provider](/img/tutorials/ai-chatbot/6.png) +![Choose your AI provider](6.png) ## Setting up your workflow {#workflow} @@ -108,11 +108,11 @@ Within your development workspace, open Workflow Builder by clicking on your wor Click **Untitled Workflow** at the top to rename your workflow. For this tutorial, we'll call the workflow **Welcome to the channel**. Enter a description, such as _Summarizes channels for new members_, and click **Save**. -![Setting up a new workflow](/img/tutorials/ai-chatbot/1.png) +![Setting up a new workflow](1.png) Select **Choose an event** under **Start the workflow...**, and then choose **When a person joins a channel**. Select the channel name from the drop-down menu and click **Save**. -![Start the workflow](/img/tutorials/ai-chatbot/2.png) +![Start the workflow](2.png) Under **Then, do these things**, click **Add steps** and complete the following: @@ -121,20 +121,20 @@ Under **Then, do these things**, click **Add steps** and complete the following: 3. Under **Add a message**, enter a short message, such as _Hi! Welcome to `{}The channel that the user joined`. Would you like a summary of the recent conversation?_ Note that the _`{}The channel that the user joined`_ is a variable; you can insert it by selecting **{}Insert a variable** at the bottom of the message text box. 4. Select the **Add Button** button, and name the button _Yes, give me a summary_. Click **Done**. -![Send a message](/img/tutorials/ai-chatbot/3.png) +![Send a message](3.png) We'll add two more steps under the **Then, do these things** section. First, scroll to the bottom of the list of steps and choose **Custom**, then choose **Bolty** and **Bolty Custom Function**. In the **Channel** drop-down menu, select **Channel that the user joined**. Click **Save**. -![Bolty custom function](/img/tutorials/ai-chatbot/4.png) +![Bolty custom function](4.png) For the final step, complete the following: 1. Choose **Messages** and then **Send a message to a person**. Under **Select a member**, choose **Person who clicked the button** from the drop-down menu. 2. Under **Add a message**, click **Insert a variable** and choose **`{}Summary`** under the **Bolty Custom Function** section in the list that appears. Click **Save**. -![Summary](/img/tutorials/ai-chatbot/5.png) +![Summary](5.png) When finished, click **Finish Up**, then click **Publish** to make the workflow available in your workspace. @@ -149,9 +149,9 @@ In order for Bolty to provide summaries of recent conversation in a channel, Bol To test this, leave the channel you just invited Bolty to and rejoin it. This will kick off your workflow and you'll receive a direct message from **Welcome to the channel**. Click the **Yes, give me a summary** button, and Bolty will summarize the recent conversations in the channel you joined. -![Channel summary](/img/tutorials/ai-chatbot/7.png) +![Channel summary](7.png) -The central part of this functionality is shown in the following code snippet. Note the use of the [`user_context`](https://tools.slack.dev/deno-slack-sdk/reference/slack-types#usercontext) object, a Slack type that represents the user who is interacting with our workflow, as well as the `history` of the channel that will be summarized, which includes the ten most recent messages. +The central part of this functionality is shown in the following code snippet. Note the use of the [`user_context`](/tools/deno-slack-sdk/reference/slack-types#usercontext) object, a Slack type that represents the user who is interacting with our workflow, as well as the `history` of the channel that will be summarized, which includes the ten most recent messages. ```python from ai.providers import get_provider_response @@ -191,12 +191,12 @@ To ask Bolty a question, you can chat with Bolty in any channel the app is in. U You can also navigate to **Bolty** in your **Apps** list and select the **Messages** tab to chat with Bolty directly. -![Ask Bolty](/img/tutorials/ai-chatbot/8.png) +![Ask Bolty](8.png) ## Next steps {#next-steps} Congratulations! You've successfully integrated the power of AI into your workspace. Check out these links to take the next steps in your Bolt for Python journey. -* To learn more about Bolt for Python, refer to the [Getting started](../getting-started) documentation. -* For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](https://docs.slack.dev/workflows/workflow-steps) guide. -* To use the Bolt for Python SDK to develop on the automations platform, refer to the [Create a workflow step for Workflow Builder: Bolt for Python](/bolt-python/tutorial/custom-steps) tutorial. +* To learn more about Bolt for Python, refer to the [Getting started](/tools/bolt-python/getting-started) documentation. +* For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. +* To use the Bolt for Python SDK to develop on the automations platform, refer to the [Create a workflow step for Workflow Builder: Bolt for Python](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) tutorial. diff --git a/docs/static/img/tutorials/custom-steps-jira/1.png b/docs/english/tutorial/custom-steps-for-jira/1.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-jira/1.png rename to docs/english/tutorial/custom-steps-for-jira/1.png diff --git a/docs/static/img/tutorials/custom-steps-jira/2.png b/docs/english/tutorial/custom-steps-for-jira/2.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-jira/2.png rename to docs/english/tutorial/custom-steps-for-jira/2.png diff --git a/docs/static/img/tutorials/custom-steps-jira/3.png b/docs/english/tutorial/custom-steps-for-jira/3.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-jira/3.png rename to docs/english/tutorial/custom-steps-for-jira/3.png diff --git a/docs/static/img/tutorials/custom-steps-jira/4.png b/docs/english/tutorial/custom-steps-for-jira/4.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-jira/4.png rename to docs/english/tutorial/custom-steps-for-jira/4.png diff --git a/docs/static/img/tutorials/custom-steps-jira/5.png b/docs/english/tutorial/custom-steps-for-jira/5.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-jira/5.png rename to docs/english/tutorial/custom-steps-for-jira/5.png diff --git a/docs/static/img/tutorials/custom-steps-jira/6.png b/docs/english/tutorial/custom-steps-for-jira/6.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-jira/6.png rename to docs/english/tutorial/custom-steps-for-jira/6.png diff --git a/docs/static/img/tutorials/custom-steps-jira/7.png b/docs/english/tutorial/custom-steps-for-jira/7.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-jira/7.png rename to docs/english/tutorial/custom-steps-for-jira/7.png diff --git a/docs/content/tutorial/custom-steps-for-jira.md b/docs/english/tutorial/custom-steps-for-jira/custom-steps-for-jira.md similarity index 86% rename from docs/content/tutorial/custom-steps-for-jira.md rename to docs/english/tutorial/custom-steps-for-jira/custom-steps-for-jira.md index b38f9337c..f310e75cc 100644 --- a/docs/content/tutorial/custom-steps-for-jira.md +++ b/docs/english/tutorial/custom-steps-for-jira/custom-steps-for-jira.md @@ -11,7 +11,7 @@ In this tutorial, you'll learn how to configure custom steps for use with JIRA. Before getting started, you will need the following: -* a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now—you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. +* a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now—you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. * a development environment with [Python 3.6](https://www.python.org/downloads/) or later. **Skip to the code** @@ -35,7 +35,7 @@ https://github.com/slack-samples/bolt-python-jira-functions/blob/main/manifest.j Before you'll be able to successfully run the app, you'll need to obtain and set some environment variables. 1. Once you have installed the app to your workspace, copy the **Bot User OAuth Token** from the **Install App** page. You will store this in your environment as `SLACK_BOT_TOKEN` (we'll get to that next). -2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](https://docs.slack.dev/reference/scopes/connections.write) scope, name the token, and click **Generate**. Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`. +2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](/reference/scopes/connections.write) scope, name the token, and click **Generate**. Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`. 3. Follow [these instructions](https://confluence.atlassian.com/adminjiraserver0909/configure-an-incoming-link-1251415519.html) to create an external app link and to generate its redirect URL (the base of which will be stored as your APP_BASE_URL variable below), client ID, and client secret. 4. Run the following commands in your terminal to store your environment variables, client ID, and client secret. 5. You'll also need to know your team ID (found by opening your Slack instance in a web browser and copying the value within the link that starts with the letter **T**) and your app ID (found under **Basic Information**). @@ -127,21 +127,21 @@ If your app is up and running, you'll see a message noting that the app is start 2. Select **New Workflow** > **Build Workflow**. 3. Click **Untitled Workflow** at the top of the pane to rename your workflow. We'll call it **Create Issue**. For the description, enter _Creates a new issue_, then click **Save**. -![Workflow details](/img/tutorials/custom-steps-jira/1.png) +![Workflow details](1.png) 4. Select **Choose an event** under **Start the workflow...**, and then select **From a link in Slack**. Click **Continue**. -![Start the workflow](/img/tutorials/custom-steps-jira/2.png) +![Start the workflow](2.png) 5. Under **Then, do these things** click **Add steps** to add the custom step. Your custom step will be the function defined in the [`create_issue.py`](https://github.com/slack-samples/bolt-python-jira-functions/blob/main/listeners/functions/create_issue.py) file. Scroll down to the bottom of the list on the right-hand pane and select **Custom**, then **BoltPy Jira Functions** > **Create an issue**. Enter the project details, issue type (optional), summary (optional), and description (optional). Click **Save**. -![Custom function](/img/tutorials/custom-steps-jira/3.png) +![Custom function](3.png) 6. Add another step and select **Messages** > **Send a message to a channel**. Select **Channel where the workflow was used** from the drop-down list and then select **Insert a variable** and **Issue url**. Click **Save**. -![Insert variable for issue URL](/img/tutorials/custom-steps-jira/4.png) +![Insert variable for issue URL](4.png) 7. Click **Publish** to make the workflow available to your workspace. @@ -150,16 +150,16 @@ If your app is up and running, you'll see a message noting that the app is start 1. Copy your workflow link. 2. Navigate to your app's home tab and click **Connect an Account** to connect your JIRA account to the app. -![Connect account](/img/tutorials/custom-steps-jira/5.png) +![Connect account](5.png) 3. Click **Allow** on the screen that appears. -![Allow connection](/img/tutorials/custom-steps-jira/6.png) +![Allow connection](6.png) 4. In any channel, post the workflow link you copied. 5. Click **Start Workflow** and observe as the link to a new JIRA ticket is posted in the channel. Click the link to be directed to the newly-created issue within your JIRA project. -![JIRA issue](/img/tutorials/custom-steps-jira/7.png) +![JIRA issue](7.png) When finished, you can click the **Disconnect Account** button in the home tab to disconnect your app from your JIRA account. @@ -167,6 +167,6 @@ When finished, you can click the **Disconnect Account** button in the home tab t Congratulations! You've successfully customized your workspace with custom steps in Workflow Builder. Check out these links to take the next steps in your journey. -* To learn more about Bolt for Python, refer to the [getting started](/getting-started) documentation. -* For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](https://docs.slack.dev/workflows/workflow-steps) guide. -* For information about custom steps dynamic options, refer to [custom steps dynamic options in Workflow Builder](https://docs.slack.dev/workflows/creating-custom-steps-dynamic-options). +* To learn more about Bolt for Python, refer to the [getting started](/tools/bolt-python/getting-started) documentation. +* For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. +* For information about custom steps dynamic options, refer to [custom steps dynamic options in Workflow Builder](/tools/bolt-python/concepts/custom-steps-dynamic-options). diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/add-step.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/add-step.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/add-step.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/add-step.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/app-message.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/app-message.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/app-message.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/app-message.png diff --git a/docs/content/tutorial/custom-steps-workflow-builder-existing.md b/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md similarity index 91% rename from docs/content/tutorial/custom-steps-workflow-builder-existing.md rename to docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md index e5c584a4c..0441b033c 100644 --- a/docs/content/tutorial/custom-steps-workflow-builder-existing.md +++ b/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md @@ -1,12 +1,10 @@ ---- -title: Custom Steps for Workflow Builder (existing app) ---- +# Custom Steps for Workflow Builder (existing app) :::info[This feature requires a paid plan] If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. ::: -If you followed along with our [create a custom step for Workflow Builder: new app](/tutorial/custom-steps-workflow-builder-new) tutorial, you have seen how to add custom steps to a brand new app. But what if you have an app up and running currently to which you'd like to add custom steps? You've come to the right place! +If you followed along with our [create a custom step for Workflow Builder: new app](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) tutorial, you have seen how to add custom steps to a brand new app. But what if you have an app up and running currently to which you'd like to add custom steps? You've come to the right place! In this tutorial we will: - Start with an existing Bolt app @@ -28,7 +26,7 @@ In order to add custom workflow steps to an app, the app also needs to be org-re Navigate to **Org Level Apps** in the left nav and click **Opt-In**, then confirm **Yes, Opt-In**. -![Make your app org-ready](/img/tutorials/custom-steps-wfb-existing/org-ready.png) +![Make your app org-ready](org-ready.png) ## Adding a new workflow step {#add-step} @@ -54,19 +52,19 @@ Navigate to **App Manifest** in the left nav and add the `function_executed` eve Navigate to **Workflow Steps** in the left nav and click **Add Step**. This is where we'll configure our step's inputs, outputs, name, and description. -![Add step](/img/tutorials/custom-steps-wfb-existing/add-step.png) +![Add step](add-step.png) For illustration purposes in this tutorial, we're going to write a custom step called Request Time Off. When the step is invoked, a message will be sent to the provided manager with an option to approve or deny the time-off request. When the manager takes an action (approves or denies the request), a message is posted with the decision and the manager who made the decision. The step will take two user IDs as inputs, representing the requesting user and their manager, and it will output both of those user IDs as well as the decision made. Add the pertinent details to the step: -![Define step](/img/tutorials/custom-steps-wfb-existing/define-step.png) +![Define step](define-step.png) Remember this `callback_id`. We will use this later when implementing a function listener. Then add the input and output parameters: -![Add inputs](/img/tutorials/custom-steps-wfb-existing/inputs.png) +![Add inputs](inputs.png) -![Add outputs](/img/tutorials/custom-steps-wfb-existing/outputs.png) +![Add outputs](outputs.png) Save your changes. @@ -260,11 +258,11 @@ Click the button to create a **New Workflow**, then **Build Workflow**. Choose t In the **Steps** pane to the right, search for your app name and locate the **Request time off** step we created. -![Find step](/img/tutorials/custom-steps-wfb-existing/find-step.png) +![Find step](find-step.png) Select the step and choose the desired inputs and click **Save**. -![Step inputs](/img/tutorials/custom-steps-wfb-existing/step-inputs.png) +![Step inputs](step-inputs.png) Next, click **Finish Up**, give your workflow a name and description, then click **Publish**. Copy the link for your workflow on the next screen, then click **Done**. @@ -272,12 +270,12 @@ Next, click **Finish Up**, give your workflow a name and description, then click In any channel where your app is installed, paste the link you copied and send it as a message. The link will unfurl into a button to start the workflow. Click the button to start the workflow. If you set yourself up as the manager, you will then see a message from your app. Pressing either button will return a confirmation or denial of your time off request. -![Message](/img/tutorials/custom-steps-wfb-existing/app-message.png) +![Message](app-message.png) ## Next steps {#next-steps} Nice work! Now that you've added a workflow step to your Bolt app, a world of possibilities is open to you! Create and share workflow steps across your organization to optimize Slack users' time and make their working lives more productive. -If you're looking to create a brand new Bolt app with custom workflow steps, check out [the tutorial here](/tutorial/custom-steps-workflow-builder-new). +If you're looking to create a brand new Bolt app with custom workflow steps, check out [the tutorial here](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new). -If you're interested in exploring how to create custom steps to use in Workflow Builder as steps with our Deno Slack SDK, too, that tutorial can be found [here](https://tools.slack.dev/deno-slack-sdk/tutorials/workflow-builder-custom-step/). +If you're interested in exploring how to create custom steps to use in Workflow Builder as steps with our Deno Slack SDK, too, that tutorial can be found [here](/tools/deno-slack-sdk/tutorials/workflow-builder-custom-step/). diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/define-step.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/define-step.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/define-step.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/define-step.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/find-step.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/find-step.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/find-step.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/find-step.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/inputs.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/inputs.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/inputs.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/inputs.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/org-ready.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/org-ready.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/org-ready.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/org-ready.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/outputs.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/outputs.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/outputs.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/outputs.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-existing/step-inputs.png b/docs/english/tutorial/custom-steps-workflow-builder-existing/step-inputs.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-existing/step-inputs.png rename to docs/english/tutorial/custom-steps-workflow-builder-existing/step-inputs.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/app-token.png b/docs/english/tutorial/custom-steps-workflow-builder-new/app-token.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/app-token.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/app-token.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/bot-token.png b/docs/english/tutorial/custom-steps-workflow-builder-new/bot-token.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/bot-token.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/bot-token.png diff --git a/docs/content/tutorial/custom-steps-workflow-builder-new.md b/docs/english/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new.md similarity index 90% rename from docs/content/tutorial/custom-steps-workflow-builder-new.md rename to docs/english/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new.md index 9d01b8676..1dceed45a 100644 --- a/docs/content/tutorial/custom-steps-workflow-builder-new.md +++ b/docs/english/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new.md @@ -1,12 +1,10 @@ ---- -title: Custom Steps for Workflow Builder (new app) ---- +# Custom Steps for Workflow Builder (new app) :::info[This feature requires a paid plan] If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. ::: -Adding a workflow step to your app and implementing a corresponding function listener is how you define a custom Workflow Builder step. In this tutorial, you'll use [Bolt for Python](/bolt-python/) to add your workflow step, then wire it up in [Workflow Builder](https://slack.com/help/articles/360035692513-Guide-to-Workflow-Builder). +Adding a workflow step to your app and implementing a corresponding function listener is how you define a custom Workflow Builder step. In this tutorial, you'll use [Bolt for Python](/tools/bolt-python/) to add your workflow step, then wire it up in [Workflow Builder](https://slack.com/help/articles/360035692513-Guide-to-Workflow-Builder). When finished, you'll be ready to build scalable and innovative workflow steps for anyone using Workflow Builder in your workspace. @@ -58,7 +56,7 @@ We now have a Bolt app ready for development! Open the `manifest.json` file and Open a browser and navigate to [your apps page](https://api.slack.com/apps). This is where we will create a new app with our previously copied manifest details. Click the **Create New App** button, then select **From an app manifest** when prompted to choose how you'd like to configure your app's settings. -![Create app from manifest](/img/tutorials/custom-steps-wfb-new/manifest.png) +![Create app from manifest](manifest.png) Next, select a workspace where you have permissions to install apps, and click **Next**. Select the **JSON** tab and clear the existing contents. Paste the contents of the `manifest.json` file you previously copied. @@ -70,11 +68,11 @@ All of your app's settings can be configured within these screens. By creating a Navigate to **Event Subscriptions** and expand **Subscribe to bot events** to see that we have subscribed to the `function_executed` event. This is also a requirement for adding workflow steps to our app, as it lets our app know when a step has been triggered, allowing our app to respond to it. -Another configuration setting to note is **Socket Mode**. We have turned this on for our local development, but socket mode is not intended for use in a production environment. When you are satisfied with your app and ready to deploy it to a production environment, you should switch to using public HTTP request URLs. Read more about getting started with HTTP in [Bolt for Python here](/bolt-python/getting-started). +Another configuration setting to note is **Socket Mode**. We have turned this on for our local development, but socket mode is not intended for use in a production environment. When you are satisfied with your app and ready to deploy it to a production environment, you should switch to using public HTTP request URLs. Read more about getting started with HTTP in [Bolt for Python here](/tools/bolt-python/getting-started). Clicking on **Workflow Steps** in the left nav will show you that one workflow step has been added! This reflects the `function` defined in our manifest: functions are workflow steps. We will get to this step's implementation later. -![Workflow step](/img/tutorials/custom-steps-wfb-new/workflow-step.png) +![Workflow step](workflow-step.png) ### Tokens {#tokens} @@ -85,17 +83,17 @@ In order to connect our app here with the logic of our sample code set up locall To generate an app token, navigate to **Basic Information** and scroll down to **App-Level Token**. -![App token](/img/tutorials/custom-steps-wfb-new/app-token.png) +![App token](app-token.png) Click **Generate Token and Scopes**, then **Add Scope** and choose `connections:write`. Choose a name for your token and click **Generate**. Copy that value, save it somewhere accessible, and click **Done** to close out of the modal. Next up is the bot token. We can only get this token by installing the app into the workspace. Navigate to **Install App** and click the button to install, choosing **Allow** at the next screen. -![Install app](/img/tutorials/custom-steps-wfb-new/install.png) +![Install app](install.png) You will then have a bot token. Again, copy that value and save it somewhere accessible. -![Bot token](/img/tutorials/custom-steps-wfb-new/bot-token.png) +![Bot token](bot-token.png) 💡 Treat your tokens like passwords and keep them safe. Your app uses them to post and retrieve information from Slack workspaces. Minimally, do NOT commit them to version control. @@ -120,8 +118,7 @@ You'll know the local development server is up and running successfully when it With your development server running, continue to the next step. -:::info -If you need to stop running the local development server, press `` + `c` to end the process. +:::info[If you need to stop running the local development server, press `` + `c` to end the process.] ::: ## Wiring up the sample step in Workflow Builder {#wfb} @@ -130,15 +127,15 @@ The starter project you cloned contains a sample custom step lovingly titled “ In the Slack Client of your development workspace, open Workflow Builder by clicking on the workspace name, **Tools**, then **Workflow Builder**. Create a new workflow, then select **Build Workflow**: -![Creating a new workflow](/img/tutorials/custom-steps-wfb-new/wfb-1.png) +![Creating a new workflow](wfb-1.png) Select **Choose an event** under **Start the workflow...**, then **From a link in Slack** to configure this workflow to start when someone clicks its shortcut link: -![Starting a new workflow from a shortcut link](/img/tutorials/custom-steps-wfb-new/wfb-2.png) +![Starting a new workflow from a shortcut link](wfb-2.png) Click the **Continue** button to confirm that this is workflow should start with a shortcut link: -![Confirming a new shortcut workflow setup](/img/tutorials/custom-steps-wfb-new/wfb-3.png) +![Confirming a new shortcut workflow setup](wfb-3.png) Find the sample step provided in the template by either searching for the name of your app (e.g., `Bolt Custom Step`) or the name of your step (e.g. `Sample step`) in the Steps search bar. @@ -146,43 +143,43 @@ If you search by app name, any custom step that your app has defined will be lis Add the “Sample step" in the search results to the workflow: -![Adding the sample step to the workflow](/img/tutorials/custom-steps-wfb-new/wfb-4.png) +![Adding the sample step to the workflow](wfb-4.png) As soon as you add the “Sample step" to the workflow, a modal will appear to configure the step's input—in this case, a user variable: -![Configuring the sample step's inputs](/img/tutorials/custom-steps-wfb-new/wfb-5.png) +![Configuring the sample step's inputs](wfb-5.png) Configure the user input to be “Person who used this workflow”, then click the **Save** button: -![Saving the sample step after configuring the user input](/img/tutorials/custom-steps-wfb-new/wfb-6.png) +![Saving the sample step after configuring the user input](wfb-6.png) Click the **Finish Up** button, then provide a name and description for your workflow. Finally, click the **Publish** button: -![Publishing a workflow](/img/tutorials/custom-steps-wfb-new/wfb-7.png) +![Publishing a workflow](wfb-7.png) Copy the shortcut link, then exit Workflow Builder and paste the link to a message in any channel you’re in: -![Copying a workflow link](/img/tutorials/custom-steps-wfb-new/wfb-8.png) +![Copying a workflow link](wfb-8.png) After you send a message containing the shortcut link, the link will unfurl and you’ll see a **Start Workflow** button. Click the **Start Workflow** button: -![Starting your new workflow](/img/tutorials/custom-steps-wfb-new/wfb-9.png) +![Starting your new workflow](wfb-9.png) You should see a new direct message from your app: -![A new direct message from your app](/img/tutorials/custom-steps-wfb-new/wfb-10.png) +![A new direct message from your app](wfb-10.png) The message from your app asks you to click the **Complete step** button: -![A new direct message from your app](/img/tutorials/custom-steps-wfb-new/wfb-11.png) +![A new direct message from your app](wfb-11.png) Once you click the button, the direct message to you will be updated to let you know that the step interaction was successfully completed: -![Sample step finished successfully](/img/tutorials/custom-steps-wfb-new/wfb-12.png) +![Sample step finished successfully](wfb-12.png) Now that we’ve gotten a feel for how we will use the custom step, let’s learn more about how function listeners work. @@ -354,6 +351,6 @@ Slack will send an action event payload to your app when the button is clicked o That's it — we hope you learned a lot! -In this tutorial, we added custom steps via the manifest, but if you'd like to see how to add custom steps in the [app settings](https://api.slack.com/apps) to an existing app, follow along with the [Create a custom step for Workflow Builder: existing Bolt app](/tutorials/custom-steps-workflow-builder-existing) tutorial. +In this tutorial, we added custom steps via the manifest, but if you'd like to see how to add custom steps in the [app settings](https://api.slack.com/apps) to an existing app, follow along with the [Create a custom step for Workflow Builder: existing Bolt app](/tools/bolt-python/tutorial/custom-steps-workflow-builder-existing) tutorial. -If you're interested in exploring how to create custom steps to use in Workflow Builder as steps with our Deno Slack SDK, too, that tutorial can be found [here](https://tools.slack.dev/deno-slack-sdk/tutorials/workflow-builder-custom-step/). +If you're interested in exploring how to create custom steps to use in Workflow Builder as steps with our Deno Slack SDK, too, that tutorial can be found [here](/tools/deno-slack-sdk/tutorials/workflow-builder-custom-step/). diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/install.png b/docs/english/tutorial/custom-steps-workflow-builder-new/install.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/install.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/install.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/manifest.png b/docs/english/tutorial/custom-steps-workflow-builder-new/manifest.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/manifest.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/manifest.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-1.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-1.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-1.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-1.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-10.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-10.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-10.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-10.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-11.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-11.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-11.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-11.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-12.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-12.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-12.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-12.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-2.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-2.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-2.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-2.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-3.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-3.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-3.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-3.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-4.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-4.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-4.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-4.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-5.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-5.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-5.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-5.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-6.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-6.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-6.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-6.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-7.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-7.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-7.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-7.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-8.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-8.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-8.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-8.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/wfb-9.png b/docs/english/tutorial/custom-steps-workflow-builder-new/wfb-9.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/wfb-9.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/wfb-9.png diff --git a/docs/static/img/tutorials/custom-steps-wfb-new/workflow-step.png b/docs/english/tutorial/custom-steps-workflow-builder-new/workflow-step.png similarity index 100% rename from docs/static/img/tutorials/custom-steps-wfb-new/workflow-step.png rename to docs/english/tutorial/custom-steps-workflow-builder-new/workflow-step.png diff --git a/docs/content/tutorial/custom-steps.md b/docs/english/tutorial/custom-steps.md similarity index 94% rename from docs/content/tutorial/custom-steps.md rename to docs/english/tutorial/custom-steps.md index 2486a49ef..66dc16198 100644 --- a/docs/content/tutorial/custom-steps.md +++ b/docs/english/tutorial/custom-steps.md @@ -9,7 +9,7 @@ If you don't have a paid workspace for development, you can join the [Developer With custom steps for Bolt apps, your app can create and process workflow steps that users later add in Workflow Builder. This guide goes through how to build a custom step for your app using the [app settings](https://api.slack.com/apps). -If you're looking to build a custom step using the Deno Slack SDK, check out our guide on [creating a custom step for Workflow Builder with the Deno Slack SDK](https://tools.slack.dev/deno-slack-sdk/tutorials/workflow-builder-custom-step/). +If you're looking to build a custom step using the Deno Slack SDK, check out our guide on [creating a custom step for Workflow Builder with the Deno Slack SDK](/tools/deno-slack-sdk/tutorials/workflow-builder-custom-step/). You can also take a look at the template for the [Bolt for Python custom workflow step](https://github.com/slack-samples/bolt-python-custom-step-template) on GitHub. @@ -69,7 +69,7 @@ Field | Type | Description `type` | String | Defines the data type and can fall into one of two categories: primitives or Slack-specific. `title` | String | The label that appears in Workflow Builder when a user sets up this step in their workflow. `description` | String | The description that accompanies the input when a user sets up this step in their workflow. -`dynamic_options` | Object | For custom steps dynamic options in Workflow Builder, define this property and point to a custom step designed to return the set of dynamic elements once the step is added to a workflow within Workflow Builder. Dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. Refer to custom steps dynamic options for Workflow Builder using [Bolt for JavaScript](https://tools.slack.dev/bolt-js/concepts/custom-steps-dynamic-options/) or [Bolt for Python](https://tools.slack.dev/bolt-python/concepts/custom-steps-dynamic-options/) for more details. +`dynamic_options` | Object | For custom steps dynamic options in Workflow Builder, define this property and point to a custom step designed to return the set of dynamic elements once the step is added to a workflow within Workflow Builder. Dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. Refer to custom steps dynamic options for Workflow Builder using [Bolt for JavaScript](/tools/bolt-js/concepts/custom-steps-dynamic-options/) or [Bolt for Python](https://docs.slack.dev/tools/bolt-python/concepts/custom-steps-dynamic-options/) for more details. `is_required` | Boolean | Indicates whether or not the input is required by the step in order to run. If it’s required and not provided, the user will not be able to save the configuration nor use the step in their workflow. This property is available only in v1 of the manifest. We recommend v2, using the `required` array as noted in the example above. `hint` | String | Helper text that appears below the input when a user sets up this step in their workflow. @@ -225,7 +225,7 @@ The second argument is the callback function, or the logic that will run when yo Field | Description ------|------------ -`client` | A `WebClient` instance used to make things happen in Slack. From sending messages to opening modals, `client` makes it all happen. For a full list of available methods, refer to the [Web API methods](/methods). Read more about the `WebClient` for Bolt Python [here](https://tools.slack.dev/bolt-python/concepts/web-api/). +`client` | A `WebClient` instance used to make things happen in Slack. From sending messages to opening modals, `client` makes it all happen. For a full list of available methods, refer to the [Web API methods](/reference/methods). Read more about the `WebClient` for Bolt Python [here](https://docs.slack.dev/tools/bolt-python/concepts/web-api/). `complete` | A utility method that invokes `functions.completeSuccess`. This method indicates to Slack that a step has completed successfully without issue. When called, `complete` requires you include an `outputs` object that matches your step definition in [`output_parameters`](#inputs-outputs). `fail` | A utility method that invokes `functions.completeError`. True to its name, this method signals to Slack that a step has failed to complete. The `fail` method requires an argument of `error` to be sent along with it, which is used to help users understand what went wrong. `inputs` | An alias for the `input_parameters` that were provided to the step upon execution. @@ -255,7 +255,7 @@ When you're ready to deploy your steps for wider use, you'll need to decide *whe ### Control step access {#access} -You can choose who has access to your custom steps. To define this, refer to the [custom function access](/automation/functions/access) page. +You can choose who has access to your custom steps. To define this, refer to the [custom function access](/tools/deno-slack-sdk/guides/controlling-access-to-custom-functions) page. ### Distribution {#distribution} @@ -268,5 +268,5 @@ Apps containing custom steps cannot be distributed publicly or submitted to the ## Related tutorials {#tutorials} -* [Custom steps for Workflow Builder (new app)](/tutorial/custom-steps-WB-new) -* [Custom steps for Workflow Builder (existing app)](/tutorial/custom-steps-WB-existing) +* [Custom steps for Workflow Builder (new app)](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) +* [Custom steps for Workflow Builder (existing app)](/tools/bolt-python/tutorial/custom-steps-workflow-builder-existing/) \ No newline at end of file diff --git a/docs/static/img/tutorials/modals/base_link.gif b/docs/english/tutorial/modals/base_link.gif similarity index 100% rename from docs/static/img/tutorials/modals/base_link.gif rename to docs/english/tutorial/modals/base_link.gif diff --git a/docs/static/img/tutorials/modals/final_product.gif b/docs/english/tutorial/modals/final_product.gif similarity index 100% rename from docs/static/img/tutorials/modals/final_product.gif rename to docs/english/tutorial/modals/final_product.gif diff --git a/docs/static/img/tutorials/modals/heart_icon.gif b/docs/english/tutorial/modals/heart_icon.gif similarity index 100% rename from docs/static/img/tutorials/modals/heart_icon.gif rename to docs/english/tutorial/modals/heart_icon.gif diff --git a/docs/static/img/tutorials/modals/interactivity_url.png b/docs/english/tutorial/modals/interactivity_url.png similarity index 100% rename from docs/static/img/tutorials/modals/interactivity_url.png rename to docs/english/tutorial/modals/interactivity_url.png diff --git a/docs/content/tutorial/modals.md b/docs/english/tutorial/modals/modals.md similarity index 83% rename from docs/content/tutorial/modals.md rename to docs/english/tutorial/modals/modals.md index b6d672d08..ee6d1e0d8 100644 --- a/docs/content/tutorial/modals.md +++ b/docs/english/tutorial/modals/modals.md @@ -1,4 +1,3 @@ - # Modals If you're learning about Slack apps, modals, or slash commands for the first time, you've come to the right place! In this tutorial, we'll take a look at setting up your very own server using GitHub Codespaces, then using that server to run your Slack app built with the [**Bolt for Python framework**](https://github.com/SlackAPI/bolt-python). @@ -13,9 +12,9 @@ At the end of this tutorial, your final app will look like this: ![announce](https://github.com/user-attachments/assets/0bf1c2f0-4b22-4c9c-98b3-b21e9bcc14a8) And will make use of these Slack concepts: -* [**Block Kit**](https://docs.slack.dev/block-kit/) is a UI framework for Slack apps that allows you to create beautiful, interactive messages within Slack. If you've ever seen a message in Slack with buttons or a select menu, that's Block Kit. -* [**Modals**](https://docs.slack.dev/surfaces/modals) are a pop-up window that displays right in Slack. They grab the attention of the user, and are normally used to prompt users to provide some kind of information or input in a form. -* [**Slash Commands**](https://docs.slack.dev/interactivity/implementing-slash-commands) allow you to invoke your app within Slack by just typing into the message composer box. e.g. `/remind`, `/topic`. +* [**Block Kit**](/block-kit/) is a UI framework for Slack apps that allows you to create beautiful, interactive messages within Slack. If you've ever seen a message in Slack with buttons or a select menu, that's Block Kit. +* [**Modals**](/surfaces/modals) are a pop-up window that displays right in Slack. They grab the attention of the user, and are normally used to prompt users to provide some kind of information or input in a form. +* [**Slash Commands**](/interactivity/implementing-slash-commands) allow you to invoke your app within Slack by just typing into the message composer box. e.g. `/remind`, `/topic`. If you're familiar with using Heroku you can also deploy directly to Heroku with the following button. @@ -66,7 +65,7 @@ You'll need to create an app and configure it properly within App Settings befor } ``` -2. Once your app has been created, scroll down to `App-Level Tokens` and create a token that requests for the [`connections:write`](https://docs.slack.dev/reference/scopes/connections.write) scope, which allows you to use [Socket Mode](https://docs.slack.dev/apis/events-api/using-socket-mode), a secure way to develop on Slack through the use of WebSockets. Copy the value of your app token and keep it for safe-keeping. +2. Once your app has been created, scroll down to `App-Level Tokens` and create a token that requests for the [`connections:write`](/reference/scopes/connections.write) scope, which allows you to use [Socket Mode](/apis/events-api/using-socket-mode), a secure way to develop on Slack through the use of WebSockets. Copy the value of your app token and keep it for safe-keeping. 3. Install your app by heading to `Install App` in the left sidebar. Hit `Allow`, which means you're agreeing to install your app with the permissions that it is requesting. Be sure to copy the token that you receive, and keep it somewhere secret and safe. @@ -132,4 +131,4 @@ All done! 🎉 You've created your first slash command using Block Kit and modal ## Next steps {#next-steps} -If you want to learn more about Bolt for Python, refer to the [Getting Started guide](https://tools.slack.dev/bolt-python/getting-started). \ No newline at end of file +If you want to learn more about Bolt for Python, refer to the [Getting Started guide](https://docs.slack.dev/tools/bolt-python/getting-started). \ No newline at end of file diff --git a/docs/static/img/tutorials/modals/slash_command.png b/docs/english/tutorial/modals/slash_command.png similarity index 100% rename from docs/static/img/tutorials/modals/slash_command.png rename to docs/english/tutorial/modals/slash_command.png diff --git a/docs/footerConfig.js b/docs/footerConfig.js deleted file mode 100644 index 6433c049d..000000000 --- a/docs/footerConfig.js +++ /dev/null @@ -1,21 +0,0 @@ -const footer = { - links: [ - { - items: [ - { - html: ` - -
    - ©2025 Slack Technologies, LLC, a Salesforce company. All rights reserved. Various trademarks held by their respective owners. -
    - `, - }, - ], - }, - ], -}; - -module.exports = footer; diff --git a/docs/i18n/ja-jp/README.md b/docs/i18n/ja-jp/README.md deleted file mode 100644 index e23cb969b..000000000 --- a/docs/i18n/ja-jp/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# Bolt for Python Japanese documentation - -This README describes how the Japanese documentation is created. Please read the [/docs README](./docs/README) for information on _all_ the documentation. - -[Docusaurus](https://docusaurus.io) supports using different languages. Each language is a different version of the same site. The English site is the default. The English page will be viewable if the page is not translated into Japanese. - -There will be English pages on the Japanese site for any non-translated pages. Japanese readers will not miss any content, but they may be confused seeing English and Japanese mixed together. Please give us your thoughts on this setup. - -Because of this, the sidebar does not need to be updated for the Japanese documentation. It's always the same as the English documentation! - -## Testing the Japanese site. - -Please read the [/docs README](./docs/README.md) for instructions. Be sure to run the site in Japanese: - -``` -npm run start -- --locale ja-jp -``` - ---- - -## Japanese documentation files - -``` -docs/ -├── content/ -│ ├── getting-started.md -│ └── concepts -│ └── sending-message.md -├── i18n/ja-jp -│ ├── code.json -│ ├── docusaurus-theme-classic/ -│ │ ├── footer.json -│ │ └── navbar.json -│ └── docusaurus-plugin-content-docs/ -│ └── current/ -│ ├── getting-started.md -│ └── concepts -│ └── sending-message.md -``` - -The Japanese documentation is in `i18n/ja-jp/`. The folder contains `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, and `code.json`. - -### `docusaurus-plugin-content-docs` - -``` -docs/ -├── content/ (English pages) -│ ├── example-page.md -│ ├── getting-started.md -│ └── concepts -│ └── sending-message.md -├── i18n/ja-jp -│ └── docusaurus-plugin-content-docs/ -│ └── current/ (Japanese pages) -│ ├── getting-started.md -│ └── concepts -│ └── sending-message.md -``` - -If the file is not in `i18n/ja-jp/docusaurus-plugin-content-docs/current/`, then the English file will be used. In the example above, `example-page.md` is not in `i18n/ja-jp/docusaurus-plugin-content-docs/current/`. Therefore, the English version of `example-page.md` will appear on the Japanese site. - -The Japanese page file formats in `i18n/ja-jp/docusaurus-plugin-content-docs/current/` must be the same as the English page files in `docs/content/`. Please keep the file names in English (example: `sending-message.md`). - -Please provide a title in Japanese. It will show up in the sidebar. There are two options: - -``` ---- -title: こんにちは ---- - -# こんにちは - -``` - -[Read the Docusaurus documentation for info on writing pages in markdown](https://docusaurus.io/docs/markdown-features). - -### `docusaurus-theme-classic` - -``` -└── i18n/ja-jp - └── docusaurus-theme-classic/ - ├── footer.json - └── navbar.json -``` - -`docusaurus-theme-classic` You can translate site components (footer and navbar) for the Japanese site. Each JSON object has a `messages` and `description` value: - * `message` - The Japanese translation. It will be in English if not translated. - * `description` - What and where the message is. This stays in English. - -For example: - -``` -{ - "item.label.Hello": { - "message": "こんにちは", - "description": "The title of the page" - } -} -``` - -The JSON files are created with the `npm run write-translations -- --locale ja-jp` command. [Please read the Docusaurus documentation](https://docusaurus.io/docs/i18n/tutorial#translate-your-react-code) for more info. - -### `code.json` - -``` -└── i18n/ja-jp - └── code.json -``` - -The `code.json` file is similar to `docusaurus-theme-classic` JSON objects. `code.json` has translations provided by Docusaurus for site elements. - -For example: - -``` - "theme.CodeBlock.copy": { - "message": "コピー", - "description": "The copy button label on code blocks" - }, -``` - -Be careful changing `code.json`. If you change something in this repo, it will likely need to be changed in the other tools.slack.dev repos too, like the Bolt-Python repo. We want these translations to match for all tools.slack.dev sites. \ No newline at end of file diff --git a/docs/i18n/ja-jp/code.json b/docs/i18n/ja-jp/code.json deleted file mode 100644 index 2b3c80254..000000000 --- a/docs/i18n/ja-jp/code.json +++ /dev/null @@ -1,321 +0,0 @@ -{ - "theme.NotFound.title": { - "message": "ページが見つかりません", - "description": "The title of the 404 page" - }, - "theme.NotFound.p1": { - "message": "お探しのページが見つかりませんでした", - "description": "The first paragraph of the 404 page" - }, - "theme.NotFound.p2": { - "message": "このページにリンクしているサイトの所有者にリンクが壊れていることを伝えてください", - "description": "The 2nd paragraph of the 404 page" - }, - "theme.ErrorPageContent.title": { - "message": "エラーが発生しました", - "description": "The title of the fallback page when the page crashed" - }, - "theme.BackToTopButton.buttonAriaLabel": { - "message": "先頭へ戻る", - "description": "The ARIA label for the back to top button" - }, - "theme.blog.archive.title": { - "message": "アーカイブ", - "description": "The page & hero title of the blog archive page" - }, - "theme.blog.archive.description": { - "message": "アーカイブ", - "description": "The page & hero description of the blog archive page" - }, - "theme.blog.paginator.navAriaLabel": { - "message": "ブログ記事一覧のナビゲーション", - "description": "The ARIA label for the blog pagination" - }, - "theme.blog.paginator.newerEntries": { - "message": "新しい記事", - "description": "The label used to navigate to the newer blog posts page (previous page)" - }, - "theme.blog.paginator.olderEntries": { - "message": "過去の記事", - "description": "The label used to navigate to the older blog posts page (next page)" - }, - "theme.blog.post.paginator.navAriaLabel": { - "message": "ブログ記事のナビゲーション", - "description": "The ARIA label for the blog posts pagination" - }, - "theme.blog.post.paginator.newerPost": { - "message": "新しい記事", - "description": "The blog post button label to navigate to the newer/previous post" - }, - "theme.blog.post.paginator.olderPost": { - "message": "過去の記事", - "description": "The blog post button label to navigate to the older/next post" - }, - "theme.blog.post.plurals": { - "message": "{count}件", - "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" - }, - "theme.blog.tagTitle": { - "message": "「{tagName}」タグの記事が{nPosts}件あります", - "description": "The title of the page for a blog tag" - }, - "theme.tags.tagsPageLink": { - "message": "全てのタグを見る", - "description": "The label of the link targeting the tag list page" - }, - "theme.colorToggle.ariaLabel": { - "message": "ダークモードを切り替える(現在は{mode})", - "description": "The ARIA label for the navbar color mode toggle" - }, - "theme.colorToggle.ariaLabel.mode.dark": { - "message": "ダークモード", - "description": "The name for the dark color mode" - }, - "theme.colorToggle.ariaLabel.mode.light": { - "message": "ライトモード", - "description": "The name for the light color mode" - }, - "theme.docs.breadcrumbs.navAriaLabel": { - "message": "パンくずリストのナビゲーション", - "description": "The ARIA label for the breadcrumbs" - }, - "theme.docs.DocCard.categoryDescription.plurals": { - "message": "{count}項目", - "description": "The default description for a category card in the generated index about how many items this category includes" - }, - "theme.docs.paginator.navAriaLabel": { - "message": "ドキュメントページ", - "description": "The ARIA label for the docs pagination" - }, - "theme.docs.paginator.previous": { - "message": "前へ", - "description": "The label used to navigate to the previous doc" - }, - "theme.docs.paginator.next": { - "message": "次へ", - "description": "The label used to navigate to the next doc" - }, - "theme.docs.tagDocListPageTitle.nDocsTagged": { - "message": "{count}記事", - "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" - }, - "theme.docs.tagDocListPageTitle": { - "message": "「{tagName}」タグのついた{nDocsTagged}", - "description": "The title of the page for a docs tag" - }, - "theme.docs.versionBadge.label": { - "message": "バージョン: {versionLabel}" - }, - "theme.docs.versions.unreleasedVersionLabel": { - "message": "これはリリース前のバージョン{versionLabel}の{siteTitle}のドキュメントです。", - "description": "The label used to tell the user that he's browsing an unreleased doc version" - }, - "theme.docs.versions.unmaintainedVersionLabel": { - "message": "これはバージョン{versionLabel}の{siteTitle}のドキュメントで現在はメンテナンスされていません", - "description": "The label used to tell the user that he's browsing an unmaintained doc version" - }, - "theme.docs.versions.latestVersionSuggestionLabel": { - "message": "最新のドキュメントは{latestVersionLink} ({versionLabel}) を見てください", - "description": "The label used to tell the user to check the latest version" - }, - "theme.docs.versions.latestVersionLinkLabel": { - "message": "最新バージョン", - "description": "The label used for the latest version suggestion link label" - }, - "theme.common.editThisPage": { - "message": "このページを編集", - "description": "The link label to edit the current page" - }, - "theme.lastUpdated.atDate": { - "message": "{date}に", - "description": "The words used to describe on which date a page has been last updated" - }, - "theme.lastUpdated.byUser": { - "message": "{user}が", - "description": "The words used to describe by who the page has been last updated" - }, - "theme.lastUpdated.lastUpdatedAtBy": { - "message": "{atDate}{byUser}最終更新", - "description": "The sentence used to display when a page has been last updated, and by who" - }, - "theme.common.headingLinkTitle": { - "message": "{heading} への直接リンク", - "description": "Title for link to heading" - }, - "theme.navbar.mobileVersionsDropdown.label": { - "message": "他のバージョン", - "description": "The label for the navbar versions dropdown on mobile view" - }, - "theme.tags.tagsListLabel": { - "message": "タグ:", - "description": "The label alongside a tag list" - }, - "theme.admonition.caution": { - "message": "注意", - "description": "The default label used for the Caution admonition (:::caution)" - }, - "theme.admonition.danger": { - "message": "危険", - "description": "The default label used for the Danger admonition (:::danger)" - }, - "theme.admonition.info": { - "message": "備考", - "description": "The default label used for the Info admonition (:::info)" - }, - "theme.admonition.note": { - "message": "注記", - "description": "The default label used for the Note admonition (:::note)" - }, - "theme.admonition.tip": { - "message": "ヒント", - "description": "The default label used for the Tip admonition (:::tip)" - }, - "theme.admonition.warning": { - "message": "警告", - "description": "The default label used for the Warning admonition (:::warning)" - }, - "theme.AnnouncementBar.closeButtonAriaLabel": { - "message": "閉じる", - "description": "The ARIA label for close button of announcement bar" - }, - "theme.blog.sidebar.navAriaLabel": { - "message": "最近のブログ記事のナビゲーション", - "description": "The ARIA label for recent posts in the blog sidebar" - }, - "theme.CodeBlock.copied": { - "message": "コピーしました", - "description": "The copied button label on code blocks" - }, - "theme.CodeBlock.copyButtonAriaLabel": { - "message": "クリップボードにコードをコピー", - "description": "The ARIA label for copy code blocks button" - }, - "theme.CodeBlock.copy": { - "message": "コピー", - "description": "The copy button label on code blocks" - }, - "theme.CodeBlock.wordWrapToggle": { - "message": "折り返し", - "description": "The title attribute for toggle word wrapping button of code block lines" - }, - "theme.DocSidebarItem.expandCategoryAriaLabel": { - "message": "'{label}'の目次を開く", - "description": "The ARIA label to expand the sidebar category" - }, - "theme.DocSidebarItem.collapseCategoryAriaLabel": { - "message": "'{label}'の目次を隠す", - "description": "The ARIA label to collapse the sidebar category" - }, - "theme.NavBar.navAriaLabel": { - "message": "ナビゲーション", - "description": "The ARIA label for the main navigation" - }, - "theme.navbar.mobileLanguageDropdown.label": { - "message": "他の言語", - "description": "The label for the mobile language switcher dropdown" - }, - "theme.TOCCollapsible.toggleButtonLabel": { - "message": "このページの見出し", - "description": "The label used by the button on the collapsible TOC component" - }, - "theme.blog.post.readMore": { - "message": "もっと見る", - "description": "The label used in blog post item excerpts to link to full blog posts" - }, - "theme.blog.post.readMoreLabel": { - "message": "{title}についてもっと見る", - "description": "The ARIA label for the link to full blog posts from excerpts" - }, - "theme.blog.post.readingTime.plurals": { - "message": "約{readingTime}分", - "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" - }, - "theme.docs.breadcrumbs.home": { - "message": "ホームページ", - "description": "The ARIA label for the home page in the breadcrumbs" - }, - "theme.docs.sidebar.collapseButtonTitle": { - "message": "サイドバーを隠す", - "description": "The title attribute for collapse button of doc sidebar" - }, - "theme.docs.sidebar.collapseButtonAriaLabel": { - "message": "サイドバーを隠す", - "description": "The title attribute for collapse button of doc sidebar" - }, - "theme.docs.sidebar.navAriaLabel": { - "message": "ドキュメントのサイドバー", - "description": "The ARIA label for the sidebar navigation" - }, - "theme.docs.sidebar.closeSidebarButtonAriaLabel": { - "message": "ナビゲーションバーを閉じる", - "description": "The ARIA label for close button of mobile sidebar" - }, - "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { - "message": "← メインメニューに戻る", - "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" - }, - "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { - "message": "ナビゲーションバーを開く", - "description": "The ARIA label for hamburger menu button of mobile navigation" - }, - "theme.docs.sidebar.expandButtonTitle": { - "message": "サイドバーを開く", - "description": "The ARIA label and title attribute for expand button of doc sidebar" - }, - "theme.docs.sidebar.expandButtonAriaLabel": { - "message": "サイドバーを開く", - "description": "The ARIA label and title attribute for expand button of doc sidebar" - }, - "theme.ErrorPageContent.tryAgain": { - "message": "もう一度試してください", - "description": "The label of the button to try again rendering when the React error boundary captures an error" - }, - "theme.common.skipToMainContent": { - "message": "メインコンテンツまでスキップ", - "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" - }, - "theme.tags.tagsPageTitle": { - "message": "タグ", - "description": "The title of the tag list page" - }, - "theme.unlistedContent.title": { - "message": "非公開のページ", - "description": "The unlisted content banner title" - }, - "theme.unlistedContent.message": { - "message": "このページは非公開です。 検索対象外となり、このページのリンクに直接アクセスできるユーザーのみに公開されます。", - "description": "The unlisted content banner message" - }, - "theme.blog.author.pageTitle": { - "message": "{authorName} - {nPosts}", - "description": "The title of the page for a blog author" - }, - "theme.blog.authorsList.pageTitle": { - "message": "著者一覧", - "description": "The title of the authors page" - }, - "theme.blog.authorsList.viewAll": { - "message": "すべての著者を見る", - "description": "The label of the link targeting the blog authors page" - }, - "theme.blog.author.noPosts": { - "message": "この著者による投稿はまだありません。", - "description": "The text for authors with 0 blog post" - }, - "theme.contentVisibility.unlistedBanner.title": { - "message": "非公開のページ", - "description": "The unlisted content banner title" - }, - "theme.contentVisibility.unlistedBanner.message": { - "message": "このページは非公開です。 検索対象外となり、このページのリンクに直接アクセスできるユーザーのみに公開されます。", - "description": "The unlisted content banner message" - }, - "theme.contentVisibility.draftBanner.title": { - "message": "下書きのページ", - "description": "The draft content banner title" - }, - "theme.contentVisibility.draftBanner.message": { - "message": "このページは下書きです。開発環境でのみ表示され、本番環境のビルドには含まれません。", - "description": "The draft content banner message" - } -} diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current.json b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current.json deleted file mode 100644 index eb3b5be26..000000000 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "version.label": { - "message": "Next", - "description": "The label for version current" - }, - "sidebar.sidebarBoltPy.category.Basic concepts": { - "message": "基本的な概念", - "description": "The label for category Basic concepts in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Advanced concepts": { - "message": "応用コンセプト", - "description": "The label for category Advanced concepts in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.steps from apps (Deprecated)": { - "message": "ワークフローステップ 非推奨", - "description": "The label for category steps from apps (Deprecated) in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Tutorials": { - "message": "チュートリアル", - "description": "The label for category Tutorials in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.link.Code on GitHub": { - "message": "Code on GitHub", - "description": "The label for link Code on GitHub in sidebar sidebarBoltPy, linking to https://github.com/SlackAPI/bolt-python" - }, - "sidebar.sidebarBoltPy.link.Contributors Guide": { - "message": "貢献", - "description": "The label for link Contributors Guide in sidebar sidebarBoltPy, linking to https://github.com/SlackAPI/bolt-python/blob/main/.github/contributing.md" - }, - "sidebar.sidebarBoltPy.category.Guides": { - "message": "ガイド", - "description": "The label for category Guides in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Slack API calls": { - "message": "Slack API コール", - "description": "The label for category Slack API calls in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Events": { - "message": "イベント API", - "description": "The label for category Events in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.App UI & Interactivity": { - "message": "インタラクティビティ & ショートカット", - "description": "The label for category App UI & Interactivity in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.App Configuration": { - "message": "App の設定", - "description": "The label for category App Configuration in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Middleware & Context": { - "message": "ミドルウェア & コンテキスト", - "description": "The label for category Middleware & Context in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Adaptors": { - "message": "アダプター", - "description": "The label for category Adaptors in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Authorization & Security": { - "message": "認可 & セキュリティ", - "description": "The label for category Authorization & Security in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.category.Legacy": { - "message": "レガシー(非推奨)", - "description": "The label for category Legacy in sidebar sidebarBoltPy" - }, - "sidebar.sidebarBoltPy.link.Reference": { - "message": "リファレンス", - "description": "The label for link Reference in sidebar sidebarBoltPy, linking to https://tools.slack.dev/bolt-python/api-docs/slack_bolt/" - }, - "sidebar.sidebarBoltPy.link.Release notes": { - "message": "リリースノート", - "description": "The label for link Release notes in sidebar sidebarBoltPy, linking to https://github.com/slackapi/bolt-python/releases" - }, - "sidebar.sidebarBoltPy.doc.Bolt for Python": { - "message": "Bolt for Python", - "description": "The label for the doc item Bolt for Python in sidebar sidebarBoltPy, linking to the doc index" - } -} diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/app-home.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/app-home.md deleted file mode 100644 index 2dc5fd6c0..000000000 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/app-home.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: ホームタブの更新 -lang: ja-jp -slug: /concepts/app-home ---- - -ホームタブは、サイドバーや検索画面からアクセス可能なサーフェスエリアです。アプリはこのエリアを使ってユーザーごとのビューを表示することができます。アプリ設定ページで App Home の機能を有効にすると、`views.publish` API メソッドの呼び出しで `user_id` と[ビューのペイロード](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission)を指定して、ホームタブを公開・更新することができるようになります。 - -`app_home_opened` イベントをサブスクライブすると、ユーザーが App Home を開く操作をリッスンできます。 - -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 -```python -@app.event("app_home_opened") -def update_home_tab(client, event, logger): - try: - # 組み込みのクライアントを使って views.publish を呼び出す - client.views_publish( - # イベントに関連づけられたユーザー ID を使用 - user_id=event["user"], - # アプリの設定で予めホームタブが有効になっている必要がある - view={ - "type": "home", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Welcome home, <@" + event["user"] + "> :house:*" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text":"Learn how home tabs can be more useful and interactive ." - } - } - ] - } - ) - except Exception as e: - logger.error(f"Error publishing home tab: {e}") -``` \ No newline at end of file diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/web-api.md b/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/web-api.md deleted file mode 100644 index 75953b5bd..000000000 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/web-api.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Web API の使い方 -lang: ja-jp -slug: /concepts/web-api ---- - -`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 からの応答情報を含むオブジェクトが返されます。 - -Bolt の初期化に使用するトークンは `context` オブジェクトに設定されます。このトークンは、多くの Web API メソッドを呼び出す際に必要となります。 - -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 -```python -@app.message("wake me up") -def say_hello(client, message): - # 2020 年 9 月 30 日午後 11:59:59 を示す Unix エポック秒 - when_september_ends = 1601510399 - channel_id = message["channel"] - client.chat_scheduleMessage( - channel=channel_id, - post_at=when_september_ends, - text="Summer has come and passed" - ) -``` \ No newline at end of file diff --git a/docs/i18n/ja-jp/docusaurus-theme-classic/footer.json b/docs/i18n/ja-jp/docusaurus-theme-classic/footer.json deleted file mode 100644 index 5a2d1bc10..000000000 --- a/docs/i18n/ja-jp/docusaurus-theme-classic/footer.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "copyright": { - "message": "

    Made with ♡ by Slack and friends

    ", - "description": "The footer copyright" - } -} diff --git a/docs/i18n/ja-jp/docusaurus-theme-classic/navbar.json b/docs/i18n/ja-jp/docusaurus-theme-classic/navbar.json deleted file mode 100644 index 3eee009ee..000000000 --- a/docs/i18n/ja-jp/docusaurus-theme-classic/navbar.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "title": { - "message": "Slack Developer Tools", - "description": "The title in the navbar" - }, - "item.label.SDKs": { - "message": "SDKs", - "description": "Navbar item with label SDKs" - }, - "item.label.Java": { - "message": "Java", - "description": "Navbar item with label Java" - }, - "item.label.JavaScript": { - "message": "JavaScript", - "description": "Navbar item with label JavaScript" - }, - "item.label.Python": { - "message": "Python", - "description": "Navbar item with label Python" - }, - "item.label.Community": { - "message": "Community", - "description": "Navbar item with label Community" - }, - "item.label.Bolt": { - "message": "Bolt", - "description": "Navbar item with label Bolt" - }, - "item.label.API Docs": { - "message": "API Docs", - "description": "Navbar item with label API Docs" - }, - "item.label.Java Slack SDK": { - "message": "Java Slack SDK", - "description": "Navbar item with label Java Slack SDK" - }, - "item.label.Node Slack SDK": { - "message": "Node Slack SDK", - "description": "Navbar item with label Node Slack SDK" - }, - "item.label.Python Slack SDK": { - "message": "Python Slack SDK", - "description": "Navbar item with label Python Slack SDK" - }, - "item.label.Deno Slack SDK": { - "message": "Deno Slack SDK", - "description": "Navbar item with label Deno Slack SDK" - }, - "item.label.Community tools": { - "message": "Community tools", - "description": "Navbar item with label Community tools" - }, - "item.label.Slack Community": { - "message": "Slack Community", - "description": "Navbar item with label Slack Community" - }, - "item.label.Slack CLI": { - "message": "Slack CLI", - "description": "Navbar item with label Slack CLI" - } -} diff --git a/docs/static/img/boltpy/basic-information-page.png b/docs/img/basic-information-page.png similarity index 100% rename from docs/static/img/boltpy/basic-information-page.png rename to docs/img/basic-information-page.png diff --git a/docs/static/img/boltpy/bot-token.png b/docs/img/bot-token.png similarity index 100% rename from docs/static/img/boltpy/bot-token.png rename to docs/img/bot-token.png diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/acknowledge.md b/docs/japanese/concepts/acknowledge.md similarity index 69% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/acknowledge.md rename to docs/japanese/concepts/acknowledge.md index 72cc39257..2b3756009 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/acknowledge.md +++ b/docs/japanese/concepts/acknowledge.md @@ -1,18 +1,14 @@ ---- -title: リクエストの確認 -lang: ja-jp -slug: /concepts/acknowledge ---- +# リクエストの確認 アクション(action)、コマンド(command)、ショートカット(shortcut)、オプション(options)、およびモーダルからのデータ送信(view_submission)の各リクエストは、**必ず** `ack()` 関数を使って確認を行う必要があります。これによってリクエストが受信されたことが Slack に認識され、Slack のユーザーインターフェイスが適切に更新されます。 -リクエストの種類によっては、確認で通知方法が異なる場合があります。例えば、外部データソースを使用する選択メニューのオプションのリクエストに対する確認では、適切な[オプション](https://docs.slack.dev/reference/block-kit/composition-objects/option-object)のリストとともに `ack()` を呼び出します。モーダルからのデータ送信に対する確認では、 `response_action` を渡すことで[モーダルの更新](/concepts/view_submissions)などを行えます。 +リクエストの種類によっては、確認で通知方法が異なる場合があります。例えば、外部データソースを使用する選択メニューのオプションのリクエストに対する確認では、適切な[オプション](/reference/block-kit/composition-objects/option-object)のリストとともに `ack()` を呼び出します。モーダルからのデータ送信に対する確認では、 `response_action` を渡すことで[モーダルの更新](/tools/bolt-python/concepts/view-submissions)などを行えます。 確認までの猶予は 3 秒しかないため、新しいメッセージの送信やデータベースからの情報の取得といった時間のかかる処理は、`ack()` を呼び出した後で行うことをおすすめします。 - FaaS / serverless 環境を使う場合、 `ack()` するタイミングが異なります。 これに関する詳細は [Lazy listeners (FaaS)](/concepts/lazy-listeners) を参照してください。 + FaaS / serverless 環境を使う場合、 `ack()` するタイミングが異なります。 これに関する詳細は [Lazy listeners (FaaS)](/tools/bolt-python/concepts/lazy-listeners) を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 外部データを使用する選択メニューオプションに応答するサンプル @app.options("menu_selection") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/actions.md b/docs/japanese/concepts/actions.md similarity index 76% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/actions.md rename to docs/japanese/concepts/actions.md index 82d243e99..60019ebb7 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/actions.md +++ b/docs/japanese/concepts/actions.md @@ -1,8 +1,4 @@ ---- -title: アクション -lang: ja-jp -slug: /concepts/actions ---- +# アクション ## アクションのリスニング @@ -10,9 +6,9 @@ Bolt アプリは `action` メソッドを用いて、ボタンのクリック アクションは `str` 型または `re.Pattern` 型の `action_id` でフィルタリングできます。`action_id` は、Slack プラットフォーム上のインタラクティブコンポーネントを区別する一意の識別子として機能します。 -`action()` を使ったすべての例で `ack()` が使用されていることに注目してください。アクションのリスナー内では、Slack からのリクエストを受信したことを確認するために、`ack()` 関数を呼び出す必要があります。これについては、[リクエストの確認](/concepts/acknowledge)セクションで説明しています。 +`action()` を使ったすべての例で `ack()` が使用されていることに注目してください。アクションのリスナー内では、Slack からのリクエストを受信したことを確認するために、`ack()` 関数を呼び出す必要があります。これについては、[リクエストの確認](/tools/bolt-python/concepts/acknowledge)セクションで説明しています。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'approve_button' という action_id のブロックエレメントがトリガーされるたびに、このリスナーが呼び出させれる @app.action("approve_button") @@ -49,7 +45,7 @@ def update_message(ack, body, client): 2 つ目は、`respond()` を使用する方法です。これは、アクションに関連づけられた `response_url` を使ったメッセージ送信を行うためのユーティリティです。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'approve_button' という action_id のインタラクティブコンポーネントがトリガーされると、このリスナーが呼ばれる @app.action("approve_button") @@ -61,7 +57,7 @@ def approve_request(ack, say): ### respond() の利用 -`respond()` は `response_url` を使って送信するときに便利なメソッドで、これらと同じような動作をします。投稿するメッセージのペイロードには、全ての[メッセージペイロードのプロパティ](https://docs.slack.dev/messaging/#payloads)とオプションのプロパティとして `response_type`(値は `"in_channel"` または `"ephemeral"`)、`replace_original`、`delete_original`、`unfurl_links`、`unfurl_media` などを指定できます。こうすることによってアプリから送信されるメッセージは、やり取りの発生元に反映されます。 +`respond()` は `response_url` を使って送信するときに便利なメソッドで、これらと同じような動作をします。投稿するメッセージのペイロードには、全ての[メッセージペイロードのプロパティ](/messaging/#payloads)とオプションのプロパティとして `response_type`(値は `"in_channel"` または `"ephemeral"`)、`replace_original`、`delete_original`、`unfurl_links`、`unfurl_media` などを指定できます。こうすることによってアプリから送信されるメッセージは、やり取りの発生元に反映されます。 ```python # 'user_select' という action_id を持つアクションのトリガーをリッスン diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/adapters.md b/docs/japanese/concepts/adapters.md similarity index 97% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/adapters.md rename to docs/japanese/concepts/adapters.md index 78c94fca4..a58ed34a2 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/adapters.md +++ b/docs/japanese/concepts/adapters.md @@ -1,8 +1,4 @@ ---- -title: アダプター -lang: ja-jp -slug: /concepts/adapters ---- +# アダプター アダプターは Slack から届く受信リクエストの受付とパーズを担当し、それらのリクエストを `BoltRequest` の形式に変換して Bolt アプリに引き渡します。 diff --git a/docs/japanese/concepts/app-home.md b/docs/japanese/concepts/app-home.md new file mode 100644 index 000000000..d950221ad --- /dev/null +++ b/docs/japanese/concepts/app-home.md @@ -0,0 +1,40 @@ +# ホームタブの更新 + +[ホームタブ](/surfaces/app-home)は、サイドバーや検索画面からアクセス可能なサーフェスエリアです。アプリはこのエリアを使ってユーザーごとのビューを表示することができます。アプリ設定ページで App Home の機能を有効にすると、[`views.publish`](/reference/methods/views.publish) API メソッドの呼び出しで `user_id` と[ビューのペイロード](/reference/interaction-payloads/view-interactions-payload/#view_submission)を指定して、ホームタブを公開・更新することができるようになります。 + +[`app_home_opened`](/reference/events/app_home_opened) イベントをサブスクライブすると、ユーザーが App Home を開く操作をリッスンできます。 + +指定可能な引数の一覧は [モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 + +```python +@app.event("app_home_opened") +def update_home_tab(client, event, logger): + try: + # 組み込みのクライアントを使って views.publish を呼び出す + client.views_publish( + # イベントに関連づけられたユーザー ID を使用 + user_id=event["user"], + # アプリの設定で予めホームタブが有効になっている必要がある + view={ + "type": "home", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Welcome home, <@" + event["user"] + "> :house:*" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text":"Learn how home tabs can be more useful and interactive ." + } + } + ] + } + ) + except Exception as e: + logger.error(f"Error publishing home tab: {e}") +``` \ No newline at end of file diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/assistant.md b/docs/japanese/concepts/assistant.md similarity index 91% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/assistant.md rename to docs/japanese/concepts/assistant.md index 5ffcb6f10..e819f5361 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/assistant.md +++ b/docs/japanese/concepts/assistant.md @@ -1,12 +1,8 @@ ---- -title: エージェント・アシスタント -lang: en -slug: /concepts/assistant ---- +# エージェント・アシスタント -このページは、Bolt を使ってエージェント・アシスタントを実装するための方法を紹介します。この機能に関する一般的な情報については、[こちらのドキュメントページ(英語)](https://docs.slack.dev/ai/)を参照してください。 +このページは、Bolt を使ってエージェント・アシスタントを実装するための方法を紹介します。この機能に関する一般的な情報については、[こちらのドキュメントページ(英語)](/ai/)を参照してください。 -この機能を実装するためには、まず[アプリの設定画面](https://api.slack.com/apps)で **Agents & Assistants** 機能を有効にし、**OAuth & Permissions** のページで [`assistant:write`](https://docs.slack.dev/reference/scopes/assistant.write)、[chat:write](https://docs.slack.dev/reference/scopes/chat.write)、[`im:history`](https://docs.slack.dev/reference/scopes/im.history) を**ボットの**スコープに追加し、**Event Subscriptions** のページで [`assistant_thread_started`](https://docs.slack.dev/reference/events/assistant_thread_started)、[`assistant_thread_context_changed`](https://docs.slack.dev/reference/events/assistant_thread_context_changed)、[`message.im`](https://docs.slack.dev/reference/events/message.im) イベントを有効にしてください。 +この機能を実装するためには、まず[アプリの設定画面](https://api.slack.com/apps)で **Agents & Assistants** 機能を有効にし、**OAuth & Permissions** のページで [`assistant:write`](/reference/scopes/assistant.write)、[chat:write](/reference/scopes/chat.write)、[`im:history`](/reference/scopes/im.history) を**ボットの**スコープに追加し、**Event Subscriptions** のページで [`assistant_thread_started`](/reference/events/assistant_thread_started)、[`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed)、[`message.im`](/reference/events/message.im) イベントを有効にしてください。 また、この機能は Slack の有料プランでのみ利用可能です。もし開発用の有料プランのワークスペースをお持ちでない場合は、[Developer Program](https://api.slack.com/developer-program) に参加し、全ての有料プラン向け機能を利用可能なサンドボックス環境をつくることができます。 @@ -72,7 +68,7 @@ def respond_in_assistant_thread( app.use(assistant) ``` -リスナーに指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +リスナーに指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ユーザーがチャンネルの横でアシスタントスレッドを開いた場合、そのチャンネルの情報は、そのスレッドの `AssistantThreadContext` データとして保持され、 `get_thread_context` ユーティリティを使ってアクセスすることができます。Bolt がこのユーティリティを提供している理由は、後続のユーザーメッセージ投稿のイベントペイロードに最新のスレッドのコンテキスト情報は含まれないためです。そのため、アプリはコンテキスト情報が変更されたタイミングでそれを何らかの方法で保存し、後続のメッセージイベントのリスナーコードから参照できるようにする必要があります。 @@ -92,7 +88,7 @@ assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) ## アシスタントスレッドでの Block Kit インタラクション -より高度なユースケースでは、上のようなプロンプト例の提案ではなく Block Kit のボタンなどを使いたいという場合があるかもしれません。そして、後続の処理のために[構造化されたメッセージメタデータ](https://docs.slack.dev/messaging/message-metadata/)を含むメッセージを送信したいという場合もあるでしょう。 +より高度なユースケースでは、上のようなプロンプト例の提案ではなく Block Kit のボタンなどを使いたいという場合があるかもしれません。そして、後続の処理のために[構造化されたメッセージメタデータ](/messaging/message-metadata/)を含むメッセージを送信したいという場合もあるでしょう。 例えば、アプリが最初の返信で「参照しているチャンネルを要約」のようなボタンを表示し、ユーザーがそれをクリックして、より詳細な情報(例:要約するメッセージ数・日数、要約の目的など)を送信、アプリがそれを構造化されたメータデータに整理した上でリクエスト内容をボットのメッセージとして送信するようなシナリオです。 @@ -107,7 +103,7 @@ app = App( assistant = Assistant() -# リスナーに指定可能な引数の一覧は https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html を参照してください +# リスナーに指定可能な引数の一覧は https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html を参照してください @assistant.thread_started def start_assistant_thread(say: Say): diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/async.md b/docs/japanese/concepts/async.md similarity index 97% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/async.md rename to docs/japanese/concepts/async.md index 19609be89..cc38886d4 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/async.md +++ b/docs/japanese/concepts/async.md @@ -1,8 +1,4 @@ ---- -title: Async(asyncio)の使用 -lang: ja-jp -slug: /concepts/async ---- +# Async(asyncio)の使用 非同期バージョンの Bolt を使用する場合は、`App` の代わりに `AsyncApp` インスタンスをインポートして初期化します。`AsyncApp` では AIOHTTP を使って API リクエストを行うため、`aiohttp` をインストールする必要があります(`requirements.txt` に追記するか、`pip install aiohttp` を実行します)。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/authenticating-oauth.md b/docs/japanese/concepts/authenticating-oauth.md similarity index 89% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/authenticating-oauth.md rename to docs/japanese/concepts/authenticating-oauth.md index 82bbaf562..e58743f69 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/authenticating-oauth.md +++ b/docs/japanese/concepts/authenticating-oauth.md @@ -1,18 +1,14 @@ ---- -title: OAuth を使った認証 -lang: ja-jp -slug: /concepts/authenticating-oauth ---- +# OAuth を使った認証 -Slack アプリを複数のワークスペースにインストールできるようにするためには、OAuth フローを実装した上で、アクセストークンなどのインストールに関する情報をセキュアな方法で保存する必要があります。アプリを初期化する際に `client_id`、`client_secret`、`scopes`、`installation_store`、`state_store` を指定することで、OAuth のエンドポイントのルート情報や stateパラメーターの検証をBolt for Python にハンドリングさせることができます。カスタムのアダプターを実装する場合は、SDK が提供する組み込みの[OAuth ライブラリ](https://tools.slack.dev/python-slack-sdk/oauth/)を利用するのが便利です。これは Slack が開発したモジュールで、Bolt for Python 内部でも利用しています。 +Slack アプリを複数のワークスペースにインストールできるようにするためには、OAuth フローを実装した上で、アクセストークンなどのインストールに関する情報をセキュアな方法で保存する必要があります。アプリを初期化する際に `client_id`、`client_secret`、`scopes`、`installation_store`、`state_store` を指定することで、OAuth のエンドポイントのルート情報や stateパラメーターの検証をBolt for Python にハンドリングさせることができます。カスタムのアダプターを実装する場合は、SDK が提供する組み込みの[OAuth ライブラリ](/tools/python-slack-sdk/oauth/)を利用するのが便利です。これは Slack が開発したモジュールで、Bolt for Python 内部でも利用しています。 Bolt for Python によって `slack/oauth_redirect` という**リダイレクト URL** が生成されます。Slack はアプリのインストールフローを完了させたユーザーをこの URL にリダイレクトします。この**リダイレクト URL** は、アプリの設定の「**OAuth and Permissions**」であらかじめ追加しておく必要があります。この URL は、後ほど説明するように `OAuthSettings` というコンストラクタの引数で指定することもできます。 Bolt for Python は `slack/install` というルートも生成します。これはアプリを直接インストールするための「**Add to Slack**」ボタンを表示するために使われます。すでにワークスペースへのアプリのインストールが済んでいる場合に追加で各ユーザーのユーザートークンなどの情報を取得する場合や、カスタムのインストール用の URL を動的に生成したい場合などは、`oauth_settings` の `authorize_url_generator` でカスタムの URL ジェネレーターを指定することができます。 -バージョン 1.1.0 以降の Bolt for Python では、[OrG 全体へのインストール](https://docs.slack.dev/enterprise-grid/)がデフォルトでサポートされています。OrG 全体へのインストールは、アプリの設定の「**Org Level Apps**」で有効化できます。 +バージョン 1.1.0 以降の Bolt for Python では、[OrG 全体へのインストール](/enterprise-grid/)がデフォルトでサポートされています。OrG 全体へのインストールは、アプリの設定の「**Org Level Apps**」で有効化できます。 -Slack での OAuth を使ったインストールフローについて詳しくは、[API ドキュメントを参照してください](https://docs.slack.dev/authentication/installing-with-oauth)。 +Slack での OAuth を使ったインストールフローについて詳しくは、[API ドキュメントを参照してください](/authentication/installing-with-oauth)。 ```python import os diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/authorization.md b/docs/japanese/concepts/authorization.md similarity index 94% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/authorization.md rename to docs/japanese/concepts/authorization.md index 1a8797bb5..b6a14b30a 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/authorization.md +++ b/docs/japanese/concepts/authorization.md @@ -1,13 +1,9 @@ ---- -title: 認可(Authorization) -lang: ja-jp -slug: /concepts/authorization ---- +# 認可(Authorization) 認可(Authorization)は、Slack からの受信リクエストを処理するにあたって、どのようなSlack クレデンシャル (ボットトークンなど) を使用可能にするかを決定するプロセスです。 -単一のワークスペースにインストールされるアプリでは、`token` パラメーターを使って `App` のコンストラクターにボットトークンを渡すという、シンプルな方法が使えます。それに対して、複数のワークスペースにインストールされるアプリでは、次の 2 つの方法のいずれかを使用する必要があります。簡単なのは、組み込みの OAuth サポートを使用する方法です。OAuth サポートは、OAuth フロー用のURLのセットアップとstateの検証を行います。詳細は「[OAuth を使った認証](/concepts/authenticating-oauth)」セクションを参照してください。 +単一のワークスペースにインストールされるアプリでは、`token` パラメーターを使って `App` のコンストラクターにボットトークンを渡すという、シンプルな方法が使えます。それに対して、複数のワークスペースにインストールされるアプリでは、次の 2 つの方法のいずれかを使用する必要があります。簡単なのは、組み込みの OAuth サポートを使用する方法です。OAuth サポートは、OAuth フロー用のURLのセットアップとstateの検証を行います。詳細は「[OAuth を使った認証](/tools/bolt-python/concepts/authenticating-oauth)」セクションを参照してください。 よりカスタマイズできる方法として、`App` をインスタンス化する関数に`authorize` パラメーターを指定する方法があります。`authorize` 関数から返される [`AuthorizeResult` のインスタンス](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/authorization/authorize_result.py)には、どのユーザーがどこで発生させたリクエストかを示す情報が含まれます。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/commands.md b/docs/japanese/concepts/commands.md similarity index 72% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/commands.md rename to docs/japanese/concepts/commands.md index 73d262446..c89568dbe 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/commands.md +++ b/docs/japanese/concepts/commands.md @@ -1,18 +1,14 @@ ---- -title: コマンドのリスニングと応答 -lang: ja-jp -slug: /concepts/commands ---- +# コマンドのリスニングと応答 スラッシュコマンドが実行されたリクエストをリッスンするには、`command()` メソッドを使用します。このメソッドでは `str` 型の `command_name` の指定が必要です。 コマンドリクエストをアプリが受信し確認したことを Slack に通知するため、`ack()` を呼び出す必要があります。 -スラッシュコマンドに応答する方法は 2 つあります。1 つ目は `say()` を使う方法で、文字列または JSON のペイロードを渡すことができます。2 つ目は `respond()` を使う方法です。これは `response_url` がある場合に活躍します。これらの方法は[アクションへの応答](/concepts/action-respond)セクションで詳しく説明しています。 +スラッシュコマンドに応答する方法は 2 つあります。1 つ目は `say()` を使う方法で、文字列または JSON のペイロードを渡すことができます。2 つ目は `respond()` を使う方法です。これは `response_url` がある場合に活躍します。これらの方法は[アクションへの応答](/tools/bolt-python/concepts/actions)セクションで詳しく説明しています。 アプリの設定でコマンドを登録するときは、リクエスト URL の末尾に `/slack/events` をつけます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # echoコマンドは受け取ったコマンドをそのまま返す @app.command("/echo") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/context.md b/docs/japanese/concepts/context.md similarity index 96% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/context.md rename to docs/japanese/concepts/context.md index 6f4dd8257..13a287728 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/context.md +++ b/docs/japanese/concepts/context.md @@ -1,8 +1,4 @@ ---- -title: コンテキストの追加 -lang: ja-jp -slug: /concepts/context ---- +# コンテキストの追加 すべてのリスナーは `context` ディクショナリにアクセスできます。リスナーはこれを使ってリクエストの付加情報を得ることができます。受信リクエストに含まれる `user_id`、`team_id`、`channel_id`、`enterprise_id` などの情報は、Bolt によって自動的に設定されます。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/custom-adapters.md b/docs/japanese/concepts/custom-adapters.md similarity index 90% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/custom-adapters.md rename to docs/japanese/concepts/custom-adapters.md index b72d48ded..584893511 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/custom-adapters.md +++ b/docs/japanese/concepts/custom-adapters.md @@ -1,10 +1,6 @@ ---- -title: カスタムのアダプター -lang: ja-jp -slug: /concepts/custom-adapters ---- +# カスタムのアダプター -[アダプター](/concepts/adapters)はフレキシブルで、あなたが使用したいフレームワークに合わせた調整も可能です。アダプターでは、次の 2 つの要素が必須となっています。 +[アダプター](/tools/bolt-python/concepts/adapters)はフレキシブルで、あなたが使用したいフレームワークに合わせた調整も可能です。アダプターでは、次の 2 つの要素が必須となっています。 - `__init__(app:App)` : コンストラクター。Bolt の `App` のインスタンスを受け取り、保持します。 - `handle(req:Request)` : Slack からの受信リクエストを受け取り、解析を行う関数。通常は `handle()` という名前です。リクエストを [`BoltRequest`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/request/request.py) のインスタンスに合った形にして、保持している Bolt アプリに引き渡します。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/errors.md b/docs/japanese/concepts/errors.md similarity index 92% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/errors.md rename to docs/japanese/concepts/errors.md index 6d715c1d7..2e8e27f90 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/errors.md +++ b/docs/japanese/concepts/errors.md @@ -1,8 +1,4 @@ ---- -title: エラーの処理 -lang: ja-jp -slug: /concepts/errors ---- +# エラーの処理 リスナー内でエラーが発生した場合に try/except ブロックを使用して直接エラーを処理することができます。アプリに関連するエラーは、`BoltError` 型です。Slack API の呼び出しに関連するエラーは、`SlackApiError` 型となります。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/event-listening.md b/docs/japanese/concepts/event-listening.md similarity index 50% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/event-listening.md rename to docs/japanese/concepts/event-listening.md index 2790ad5b7..c13638226 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/event-listening.md +++ b/docs/japanese/concepts/event-listening.md @@ -1,14 +1,10 @@ ---- -title: イベントのリスニング -lang: ja-jp -slug: /concepts/event-listening ---- +# イベントのリスニング -`event()` メソッドを使うと、[Events API](https://docs.slack.dev/reference/events) の任意のイベントをリッスンできます。リッスンするイベントは、アプリの設定であらかじめサブスクライブしておく必要があります。これを利用することで、アプリがインストールされたワークスペースで何らかのイベント(例:ユーザーがメッセージにリアクションをつけた、ユーザーがチャンネルに参加した)が発生したときに、アプリに何らかのアクションを実行させることができます。 +`event()` メソッドを使うと、[Events API](/reference/events) の任意のイベントをリッスンできます。リッスンするイベントは、アプリの設定であらかじめサブスクライブしておく必要があります。これを利用することで、アプリがインストールされたワークスペースで何らかのイベント(例:ユーザーがメッセージにリアクションをつけた、ユーザーがチャンネルに参加した)が発生したときに、アプリに何らかのアクションを実行させることができます。 `event()` メソッドには `str` 型の `eventType` を指定する必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # ユーザーがワークスペースに参加した際に、自己紹介を促すメッセージを指定のチャンネルに送信 @app.event("team_join") @@ -23,7 +19,7 @@ def ask_for_introduction(event, say): `message()` リスナーは `event("message")` と等価の機能を提供します。 -`subtype` という追加のキーを指定して、イベントのサブタイプでフィルタリングすることもできます。よく使われるサブタイプには、`bot_message` や `message_replied` があります。詳しくは[メッセージイベントページ](https://docs.slack.dev/reference/events/message#subtypes)を参照してください。サブタイプなしのイベントだけにフィルターするために明に `None` を指定することもできます。 +`subtype` という追加のキーを指定して、イベントのサブタイプでフィルタリングすることもできます。よく使われるサブタイプには、`bot_message` や `message_replied` があります。詳しくは[メッセージイベントページ](/reference/events/message#subtypes)を参照してください。サブタイプなしのイベントだけにフィルターするために明に `None` を指定することもできます。 ```python # 変更されたすべてのメッセージに一致 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/global-middleware.md b/docs/japanese/concepts/global-middleware.md similarity index 81% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/global-middleware.md rename to docs/japanese/concepts/global-middleware.md index caace0621..884008090 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/global-middleware.md +++ b/docs/japanese/concepts/global-middleware.md @@ -1,15 +1,10 @@ ---- -title: グローバルミドルウェア -lang: ja-jp -slug: /concepts/global-middleware -order: 8 ---- +# グローバルミドルウェア グローバルミドルウェアは、すべての受信リクエストに対して、リスナーミドルウェアが呼ばれる前に実行されるものです。ミドルウェア関数を `app.use()` に渡すことで、アプリにはグローバルミドルウェアをいくつでも追加できます。ミドルウェア関数で受け取れる引数はリスナー関数と同じものに加えて`next()` 関数があります。 グローバルミドルウェアでもリスナーミドルウェアでも、次のミドルウェアに実行チェーンの制御をリレーするために、`next()` を呼び出す必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python @app.use diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/lazy-listeners.md b/docs/japanese/concepts/lazy-listeners.md similarity index 98% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/lazy-listeners.md rename to docs/japanese/concepts/lazy-listeners.md index 25eb6294c..029f61d99 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/lazy-listeners.md +++ b/docs/japanese/concepts/lazy-listeners.md @@ -1,8 +1,4 @@ ---- -title: Lazy リスナー(FaaS) -lang: ja-jp -slug: /concepts/lazy-listeners ---- +# Lazy リスナー(FaaS Lazy リスナー関数は、FaaS 環境への Slack アプリのデプロイを容易にする機能です。この機能は Bolt for Python でのみ利用可能で、他の Bolt フレームワークでこの機能に対応することは予定していません。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/listener-middleware.md b/docs/japanese/concepts/listener-middleware.md similarity index 83% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/listener-middleware.md rename to docs/japanese/concepts/listener-middleware.md index a013dde42..2b3ea9323 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/listener-middleware.md +++ b/docs/japanese/concepts/listener-middleware.md @@ -1,14 +1,10 @@ ---- -title: リスナーミドルウェア -lang: ja-jp -slug: /concepts/listener-middleware ---- +# リスナーミドルウェア リスナーミドルウェアは、それを渡したリスナーでのみ実行されるミドルウェアです。リスナーには、`middleware` パラメーターを使ってミドルウェア関数をいくつでも渡すことができます。このパラメーターには、1 つまたは複数のミドルウェア関数からなるリストを指定します。 非常にシンプルなリスナーミドルウェアの場合であれば、`next()` メソッドを呼び出す代わりに `bool` 値(処理を継続したい場合は `True`)を返すだけで済む「リスナーマッチャー」を使うとよいでしょう。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # ボットからのメッセージをフィルタリングするリスナーミドルウェア diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/logging.md b/docs/japanese/concepts/logging.md similarity index 95% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/logging.md rename to docs/japanese/concepts/logging.md index 22223fb08..3afa46539 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/logging.md +++ b/docs/japanese/concepts/logging.md @@ -1,8 +1,4 @@ ---- -title: ロギング -lang: ja-jp -slug: /concepts/logging ---- +# ロギング デフォルトでは、アプリからのログ情報は、既定の出力先に出力されます。`logging` モジュールをインポートすれば、`basicConfig()` の `level` パラメーターでrootのログレベルを変更することができます。指定できるログレベルは、重要度の低い方から `debug`、`info`、`warning`、`error`、および `critical` です。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-listening.md b/docs/japanese/concepts/message-listening.md similarity index 55% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-listening.md rename to docs/japanese/concepts/message-listening.md index e7d538e69..824ac67c8 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-listening.md +++ b/docs/japanese/concepts/message-listening.md @@ -1,14 +1,10 @@ ---- -title: メッセージのリスニング -lang: ja-jp -slug: /concepts/message-listening ---- +# メッセージのリスニング -[あなたのアプリがアクセス権限を持つ](https://docs.slack.dev/messaging/retrieving-messages)メッセージの投稿イベントをリッスンするには `message()` メソッドを利用します。このメソッドは `type` が `message` ではないイベントを処理対象から除外します。 +[あなたのアプリがアクセス権限を持つ](/messaging/retrieving-messages)メッセージの投稿イベントをリッスンするには `message()` メソッドを利用します。このメソッドは `type` が `message` ではないイベントを処理対象から除外します。 `message()` の引数には `str` 型または `re.Pattern` オブジェクトを指定できます。この条件のパターンに一致しないメッセージは除外されます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # '👋' が含まれるすべてのメッセージに一致 @app.message(":wave:") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-sending.md b/docs/japanese/concepts/message-sending.md similarity index 74% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-sending.md rename to docs/japanese/concepts/message-sending.md index fbcf0ac6b..a299144b6 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-sending.md +++ b/docs/japanese/concepts/message-sending.md @@ -1,14 +1,10 @@ ---- -title: メッセージの送信 -lang: ja-jp -slug: /concepts/message-sending ---- +# メッセージの送信 リスナー関数内では、関連づけられた会話(例:リスナー実行のトリガーとなったイベントまたはアクションの発生元の会話)がある場合はいつでも `say()` を使用できます。`say()` には文字列または JSON ペイロードを指定できます。文字列の場合、送信できるのはテキストベースの単純なメッセージです。より複雑なメッセージを送信するには JSON ペイロードを指定します。指定したメッセージのペイロードは、関連づけられた会話内のメッセージとして送信されます。 -リスナー関数の外でメッセージを送信したい場合や、より高度な処理(特定のエラーの処理など)を実行したい場合は、[Bolt インスタンスにアタッチされたクライアント](/concepts/web-api)の `client.chat_postMessage` を呼び出します。 +リスナー関数の外でメッセージを送信したい場合や、より高度な処理(特定のエラーの処理など)を実行したい場合は、[Bolt インスタンスにアタッチされたクライアント](/tools/bolt-python/concepts/web-api)の `client.chat_postMessage` を呼び出します。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'knock knock' が含まれるメッセージをリッスンし、イタリック体で 'Who's there?' と返信 @app.message("knock knock") @@ -20,7 +16,7 @@ def ask_who(message, say): `say()` は、より複雑なメッセージペイロードを受け付けるので、メッセージに機能やリッチな構造を与えることが容易です。 -リッチなメッセージレイアウトをアプリに追加する方法については、[API サイトのガイド](https://docs.slack.dev/messaging/#structure)を参照してください。また、[Block Kit ビルダー](https://api.slack.com/tools/block-kit-builder?template=1)の一般的なアプリフローのテンプレートも見てみてください。 +リッチなメッセージレイアウトをアプリに追加する方法については、[API サイトのガイド](/messaging/#structure)を参照してください。また、[Block Kit ビルダー](https://api.slack.com/tools/block-kit-builder?template=1)の一般的なアプリフローのテンプレートも見てみてください。 ```python # ユーザーが 📅 のリアクションをつけたら、日付ピッカーのついた section ブロックを送信 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/opening-modals.md b/docs/japanese/concepts/opening-modals.md similarity index 62% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/opening-modals.md rename to docs/japanese/concepts/opening-modals.md index a954a658b..68e3b947c 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/opening-modals.md +++ b/docs/japanese/concepts/opening-modals.md @@ -1,16 +1,12 @@ ---- -title: モーダルの開始 -lang: ja-jp -slug: /concepts/opening-modals ---- +# モーダルの開始 -モーダルは、ユーザーからのデータの入力を受け付けたり、動的な情報を表示したりするためのインターフェイスです。組み込みの APIクライアントの `views.open` メソッドに、有効な `trigger_id` とビューのペイロードを指定してモーダルを開始します。 +モーダルは、ユーザーからのデータの入力を受け付けたり、動的な情報を表示したりするためのインターフェイスです。組み込みの APIクライアントの `views.open` メソッドに、有効な `trigger_id` とビューのペイロードを指定してモーダルを開始します。 ショートカットの実行、ボタンを押下、選択メニューの操作などの操作の場合、Request URL に送信されるペイロードには `trigger_id` が含まれます。 -モーダルの生成方法についての詳細は、API ドキュメントを参照してください。 +モーダルの生成方法についての詳細は、API ドキュメントを参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # ショートカットの呼び出しをリッスン diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/select-menu-options.md b/docs/japanese/concepts/select-menu-options.md similarity index 68% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/select-menu-options.md rename to docs/japanese/concepts/select-menu-options.md index 598fb1cc6..1c2d41c58 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/select-menu-options.md +++ b/docs/japanese/concepts/select-menu-options.md @@ -1,20 +1,16 @@ ---- -title: オプションのリスニングと応答 -lang: ja-jp -slug: /concepts/options ---- +# オプションのリスニングと応答 -`options()` メソッドは、Slack からのオプション(セレクトメニュー内の動的な選択肢)をリクエストするペイロードをリッスンします。 [`action()` と同様に](/concepts/action-listening)、文字列型の `action_id` または制約付きオブジェクトが必要です。 +`options()` メソッドは、Slack からのオプション(セレクトメニュー内の動的な選択肢)をリクエストするペイロードをリッスンします。 [`action()` と同様に](/tools/bolt-python/concepts/actions)、文字列型の `action_id` または制約付きオブジェクトが必要です。 外部データソースを使って選択メニューをロードするためには、末部に `/slack/events` が付加された URL を Options Load URL として予め設定しておく必要があります。 `external_select` メニューでは `action_id` を指定することをおすすめしています。ただし、ダイアログを利用している場合、ダイアログが Block Kit に対応していないため、`callback_id` をフィルタリングするための制約オブジェクトを使用する必要があります。 -オプションのリクエストに応答するときは、有効なオプションを含む `options` または `option_groups` のリストとともに `ack()` を呼び出す必要があります。API サイトにある[外部データを使用する選択メニューに応答するサンプル例](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select)と、[ダイアログでの応答例](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_external)を参考にしてください。 +オプションのリクエストに応答するときは、有効なオプションを含む `options` または `option_groups` のリストとともに `ack()` を呼び出す必要があります。API サイトにある[外部データを使用する選択メニューに応答するサンプル例](/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select)と、[ダイアログでの応答例](/legacy/legacy-dialogs/#dynamic_select_elements_external)を参考にしてください。 -さらに、ユーザーが入力したキーワードに基づいたオプションを返すようフィルタリングロジックを適用することもできます。 これは `payload` という引数の ` value` の値に基づいて、それぞれのパターンで異なるオプションの一覧を返すように実装することができます。 Bolt for Python のすべてのリスナーやミドルウェアでは、[多くの有用な引数](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html)にアクセスすることができますので、チェックしてみてください。 +さらに、ユーザーが入力したキーワードに基づいたオプションを返すようフィルタリングロジックを適用することもできます。 これは `payload` という引数の ` value` の値に基づいて、それぞれのパターンで異なるオプションの一覧を返すように実装することができます。 Bolt for Python のすべてのリスナーやミドルウェアでは、[多くの有用な引数](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)にアクセスすることができますので、チェックしてみてください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 外部データを使用する選択メニューオプションに応答するサンプル例 @app.options("external_action") diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/shortcuts.md b/docs/japanese/concepts/shortcuts.md similarity index 77% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/shortcuts.md rename to docs/japanese/concepts/shortcuts.md index 995b6e0d7..d9a8ba050 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/shortcuts.md +++ b/docs/japanese/concepts/shortcuts.md @@ -1,22 +1,18 @@ ---- -title: ショートカットのリスニングと応答 -lang: ja-jp -slug: /concepts/shortcuts ---- +# ショートカットのリスニングと応答 -`shortcut()` メソッドは、[グローバルショートカット](https://docs.slack.dev/interactivity/implementing-shortcuts#global)と[メッセージショートカット](https://docs.slack.dev/interactivity/implementing-shortcuts#messages)の 2 つをサポートしています。 +`shortcut()` メソッドは、[グローバルショートカット](/interactivity/implementing-shortcuts#global)と[メッセージショートカット](/interactivity/implementing-shortcuts#messages)の 2 つをサポートしています。 ショートカットは、いつでも呼び出せるアプリのエントリーポイントを提供するものです。グローバルショートカットは Slack のテキスト入力エリアや検索ウィンドウからアクセスできます。メッセージショートカットはメッセージのコンテキストメニューからアクセスできます。アプリは、ショートカットリクエストをリッスンするために `shortcut()` メソッドを使用します。このメソッドには `str` 型または `re.Pattern` 型の `callback_id` パラメーターを指定します。 ショートカットリクエストがアプリによって確認されたことを Slack に伝えるため、`ack()` を呼び出す必要があります。 -ショートカットのペイロードには `trigger_id` が含まれます。アプリはこれを使って、ユーザーにやろうとしていることを確認するための[モーダルを開く](/concepts/opening-modals)ことができます。 +ショートカットのペイロードには `trigger_id` が含まれます。アプリはこれを使って、ユーザーにやろうとしていることを確認するための[モーダルを開く](/tools/bolt-python/concepts/opening-modals)ことができます。 アプリの設定でショートカットを登録する際は、他の URL と同じように、リクエスト URL の末尾に `/slack/events` をつけます。 -⚠️ グローバルショートカットのペイロードにはチャンネル ID が **含まれません**。アプリでチャンネル ID を取得する必要がある場合は、モーダル内に [`conversations_select`](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) エレメントを配置します。メッセージショートカットにはチャンネル ID が含まれます。 +⚠️ グローバルショートカットのペイロードにはチャンネル ID が **含まれません**。アプリでチャンネル ID を取得する必要がある場合は、モーダル内に [`conversations_select`](/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) エレメントを配置します。メッセージショートカットにはチャンネル ID が含まれます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # 'open_modal' という callback_id のショートカットをリッスン @app.shortcut("open_modal") @@ -36,7 +32,7 @@ def open_modal(ack, shortcut, client): "type": "section", "text": { "type": "mrkdwn", - "text":"About the simplest modal you could conceive of :smile:\n\nMaybe or ." + "text":"About the simplest modal you could conceive of :smile:\n\nMaybe or ." } }, { @@ -76,7 +72,7 @@ def open_modal(ack, shortcut, client): "type": "section", "text": { "type": "mrkdwn", - "text":"About the simplest modal you could conceive of :smile:\n\nMaybe or ." + "text":"About the simplest modal you could conceive of :smile:\n\nMaybe or ." } }, { diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/socket-mode.md b/docs/japanese/concepts/socket-mode.md similarity index 86% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/socket-mode.md rename to docs/japanese/concepts/socket-mode.md index 061daf853..92922d2de 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/socket-mode.md +++ b/docs/japanese/concepts/socket-mode.md @@ -1,10 +1,6 @@ ---- -title: ソケットモードの利用 -lang: ja-jp -slug: /concepts/socket-mode ---- +# ソケットモードの利用 -[ソケットモード](https://docs.slack.dev/apis/events-api/using-socket-mode)は、アプリに WebSocket での接続と、そのコネクション経由でのデータ受信を可能とします。Bolt for Python は、バージョン 1.2.0 からこれに対応しています。 +[ソケットモード](/apis/events-api/using-socket-mode)は、アプリに WebSocket での接続と、そのコネクション経由でのデータ受信を可能とします。Bolt for Python は、バージョン 1.2.0 からこれに対応しています。 ソケットモードでは、Slack からのペイロード送信を受け付けるエンドポイントをホストする HTTP サーバーを起動する代わりに WebSocket で Slack に接続し、そのコネクション経由でデータを受信します。ソケットモードを使う前に、アプリの管理画面でソケットモードの機能が有効になっていることを確認しておいてください。 @@ -40,7 +36,7 @@ if __name__ == "__main__": aiohttp のような asyncio をベースとしたアダプターを使う場合、アプリケーション全体が asyncio の async/await プログラミングモデルで実装されている必要があります。`AsyncApp` を動作させるためには `AsyncSocketModeHandler` とその async なミドルウェアやリスナーを利用します。 -`AsyncApp` の使い方についての詳細は、[Async (asyncio) の利用](/concepts/async)や、関連する[サンプルコード例](https://github.com/slackapi/bolt-python/tree/main/examples)を参考にしてください。 +`AsyncApp` の使い方についての詳細は、[Async (asyncio) の利用](/tools/bolt-python/concepts/async)や、関連する[サンプルコード例](https://github.com/slackapi/bolt-python/tree/main/examples)を参考にしてください。 ```python from slack_bolt.app.async_app import AsyncApp diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/token-rotation.md b/docs/japanese/concepts/token-rotation.md similarity index 67% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/token-rotation.md rename to docs/japanese/concepts/token-rotation.md index bc622fe98..25a0c735b 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/token-rotation.md +++ b/docs/japanese/concepts/token-rotation.md @@ -1,13 +1,9 @@ ---- -title: トークンのローテーション -lang: ja-jp -slug: /concepts/token-rotation ---- +# トークンのローテーション 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) で規定されているものです。 既存の Slack アプリではアクセストークンが無期限に存在し続けるのに対して、トークンローテーションを有効にしたアプリではアクセストークンが失効するようになります。リフレッシュトークンを利用して、アクセストークンを長期間にわたって更新し続けることができます。 -[Bolt for Python の組み込みの OAuth 機能](/concepts/authenticating-oauth) を使用していれば、Bolt for Python が自動的にトークンローテーションの処理をハンドリングします。 +[Bolt for Python の組み込みの OAuth 機能](/tools/bolt-python/concepts/authenticating-oauth) を使用していれば、Bolt for Python が自動的にトークンローテーションの処理をハンドリングします。 -トークンローテーションに関する詳細は [API ドキュメント](https://docs.slack.dev/authentication/using-token-rotation)を参照してください。 \ No newline at end of file +トークンローテーションに関する詳細は [API ドキュメント](/authentication/using-token-rotation)を参照してください。 \ No newline at end of file diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/updating-pushing-views.md b/docs/japanese/concepts/updating-pushing-views.md similarity index 60% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/updating-pushing-views.md rename to docs/japanese/concepts/updating-pushing-views.md index 42c89157e..cc32f5b69 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/updating-pushing-views.md +++ b/docs/japanese/concepts/updating-pushing-views.md @@ -1,10 +1,6 @@ ---- -title: モーダルの更新と多重表示 -lang: ja-jp -slug: /concepts/updating-pushing-views ---- +# モーダルの更新と多重表示 -モーダル内では、複数のモーダルをスタックのように重ねることができます。`views_open` という APIを呼び出すと、親となるとなるモーダルビューが追加されます。この最初の呼び出しの後、`views_update` を呼び出すことでそのビューを更新することができます。また、`views_push` を呼び出すと、親のモーダルの上にさらに新しいモーダルビューを重ねることもできます。 +モーダル内では、複数のモーダルをスタックのように重ねることができます。`views_open` という APIを呼び出すと、親となるとなるモーダルビューが追加されます。この最初の呼び出しの後、`views_update` を呼び出すことでそのビューを更新することができます。また、`views_push` を呼び出すと、親のモーダルの上にさらに新しいモーダルビューを重ねることもできます。 **`views_update`** @@ -12,11 +8,11 @@ slug: /concepts/updating-pushing-views **`views_push`** -既存のモーダルの上に新しいモーダルをスタックのように追加する場合は、組み込みのクライアントで `views_push` API を呼び出します。この API 呼び出しでは、有効な `trigger_id` と新しいビューのペイロードを指定します。`views_push` の引数は モーダルの開始 と同じです。モーダルを開いた後、このモーダルのスタックに追加できるモーダルビューは 2 つまでです。 +既存のモーダルの上に新しいモーダルをスタックのように追加する場合は、組み込みのクライアントで `views_push` API を呼び出します。この API 呼び出しでは、有効な `trigger_id` と新しいビューのペイロードを指定します。`views_push` の引数は モーダルの開始 と同じです。モーダルを開いた後、このモーダルのスタックに追加できるモーダルビューは 2 つまでです。 -モーダルの更新と多重表示に関する詳細は、API ドキュメントを参照してください。 +モーダルの更新と多重表示に関する詳細は、API ドキュメントを参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # モーダルに含まれる、`button_abc` という action_id のボタンの呼び出しをリッスン diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/view-submissions.md b/docs/japanese/concepts/view-submissions.md similarity index 72% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/view-submissions.md rename to docs/japanese/concepts/view-submissions.md index 7ad6637bb..5ae78f173 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/view-submissions.md +++ b/docs/japanese/concepts/view-submissions.md @@ -1,10 +1,6 @@ ---- -title: モーダルの送信のリスニング -lang: ja-jp -slug: /concepts/view_submissions ---- +# モーダルの送信のリスニング -モーダルのペイロードに `input` ブロックを含める場合、その入力値を受け取るために`view_submission` リクエストをリッスンする必要があります。`view_submission` リクエストのリッスンには、組み込みの`view()` メソッドを利用することができます。`view()` の引数には、`str` 型または `re.Pattern` 型の `callback_id` を指定します。 +モーダルのペイロードに `input` ブロックを含める場合、その入力値を受け取るために`view_submission` リクエストをリッスンする必要があります。`view_submission` リクエストのリッスンには、組み込みの`view()` メソッドを利用することができます。`view()` の引数には、`str` 型または `re.Pattern` 型の `callback_id` を指定します。 `input` ブロックの値にアクセスするには `state` オブジェクトを参照します。`state` 内には `values` というオブジェクトがあり、`block_id` と一意の `action_id` に紐づける形で入力値を保持しています。 @@ -23,9 +19,9 @@ def handle_submission(ack, body): # https://app.slack.com/block-kit-builder/#%7B%22type%22:%22modal%22,%22callback_id%22:%22view_1%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22My%20App%22,%22emoji%22:true%7D,%22blocks%22:%5B%5D%7D ack(response_action="update", view=build_new_view(body)) ``` -この例と同様に、モーダルでの送信リクエストに対して、エラーを表示するためのオプションもあります。 +この例と同様に、モーダルでの送信リクエストに対して、エラーを表示するためのオプションもあります。 -モーダルの送信について詳しくは、API ドキュメントを参照してください。 +モーダルの送信について詳しくは、API ドキュメントを参照してください。 --- @@ -33,7 +29,7 @@ def handle_submission(ack, body): `view_closed` リクエストをリッスンするためには `callback_id` を指定して、かつ `notify_on_close` 属性をモーダルのビューに設定する必要があります。以下のコード例をご覧ください。 -よく詳しい情報は、API ドキュメントを参照してください。 +よく詳しい情報は、API ドキュメントを参照してください。 ```python client.views_open( @@ -60,7 +56,7 @@ def handle_view_closed(ack, body, logger): logger.info(body) ``` -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 ```python # view_submission リクエストを処理 diff --git a/docs/japanese/concepts/web-api.md b/docs/japanese/concepts/web-api.md new file mode 100644 index 000000000..7a674b9b2 --- /dev/null +++ b/docs/japanese/concepts/web-api.md @@ -0,0 +1,19 @@ +# Web API の使い方 + +`app.client`、またはミドルウェア・リスナーの引数 `client` として Bolt アプリに提供されている `WebClient` は必要な権限を付与されており、これを利用することで[あらゆる Web API メソッド](/reference/methods)を呼び出すことができます。このクライアントのメソッドを呼び出すと `SlackResponse` という Slack からの応答情報を含むオブジェクトが返されます。 + +Bolt の初期化に使用するトークンは `context` オブジェクトに設定されます。このトークンは、多くの Web API メソッドを呼び出す際に必要となります。 + +指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +```python +@app.message("wake me up") +def say_hello(client, message): + # 2020 年 9 月 30 日午後 11:59:59 を示す Unix エポック秒 + when_september_ends = 1601510399 + channel_id = message["channel"] + client.chat_scheduleMessage( + channel=channel_id, + post_at=when_september_ends, + text="Summer has come and passed" + ) +``` \ No newline at end of file diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/getting-started.md b/docs/japanese/getting-started.md similarity index 80% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/getting-started.md rename to docs/japanese/getting-started.md index b29deaef8..2ec34d193 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/getting-started.md +++ b/docs/japanese/getting-started.md @@ -1,9 +1,3 @@ ---- -title: Bolt 入門ガイド -slug: getting-started -lang: ja-jp ---- - # Bolt 入門ガイド このガイドでは、Bolt for Python を使った Slack アプリの設定と起動の方法について説明します。ここで説明する手順では、まず新しい Slack アプリを作成し、ローカルの開発環境をセットアップし、Slack ワークスペースからのメッセージをリッスンして応答するアプリを開発するという流れになります。 @@ -16,9 +10,7 @@ lang: ja-jp ### アプリを作成する {#create-an-app} 最初にやるべきこと : Bolt での開発を始める前に、[Slack アプリを作成](https://api.slack.com/apps/new)します。 -:::tip - -通常の業務の妨げにならないよう、別の開発用のワークスペースを使用することをおすすめします。[新しいワークスペースは無料で作成できます](https://slack.com/get-started#create) +:::tip[通常の業務の妨げにならないよう、別の開発用のワークスペースを使用することをおすすめします。[新しいワークスペースは無料で作成できます](https://slack.com/get-started#create)] ::: @@ -26,39 +18,37 @@ lang: ja-jp このページでは、アプリの概要や重要な認証情報を確認できます。これらの情報は後ほど参照します。 -![Basic Information ページ](/img/boltpy/basic-information-page.png "Basic Information ページ") +![Basic Information ページ](/img/bolt-python/basic-information-page.png "Basic Information ページ") ひと通り確認して、アプリのアイコンと説明を追加したら、アプリのプロジェクトの構成 🔩 を始めましょう。 --- ### トークンとアプリのインストール {#tokens-and-installing-apps} -Slack アプリでは、[Slack API へのアクセスの管理に OAuth を使用します](https://docs.slack.dev/authentication/installing-with-oauth)。アプリがインストールされると、トークンが発行されます。アプリはそのトークンを使って API メソッドを呼び出すことができます。 +Slack アプリでは、[Slack API へのアクセスの管理に OAuth を使用します](/authentication/installing-with-oauth)。アプリがインストールされると、トークンが発行されます。アプリはそのトークンを使って API メソッドを呼び出すことができます。 Slack アプリで使用できるトークンには、ユーザートークン(`xoxp`)とボットトークン(`xoxb`)、アプリレベルトークン(`xapp`)の 3 種類があります。 -- [ユーザートークン](https://docs.slack.dev/authentication/tokens#user) を使用すると、アプリをインストールまたは認証したユーザーに成り代わって API メソッドを呼び出すことができます。1 つのワークスペースに複数のユーザートークンが存在する可能性があります。 -- [ボットトークン](https://docs.slack.dev/authentication/tokens#bot) はボットユーザーに関連づけられ、1 つのワークスペースでは最初に誰かがそのアプリをインストールした際に一度だけ発行されます。どのユーザーがインストールを実行しても、アプリが使用するボットトークンは同じになります。_ほとんど_のアプリで使用されるのは、ボットトークンです。 -- [アプリレベルトークン](https://docs.slack.dev/authentication/tokens#app-level) は、全ての組織(とその配下のワークスペースでの個々のユーザーによるインストール)を横断して、あなたのアプリを代理するものです。アプリレベルトークンは、アプリの WebSocket コネクションを確立するためによく使われます。 +- [ユーザートークン](/authentication/tokens#user) を使用すると、アプリをインストールまたは認証したユーザーに成り代わって API メソッドを呼び出すことができます。1 つのワークスペースに複数のユーザートークンが存在する可能性があります。 +- [ボットトークン](/authentication/tokens#bot) はボットユーザーに関連づけられ、1 つのワークスペースでは最初に誰かがそのアプリをインストールした際に一度だけ発行されます。どのユーザーがインストールを実行しても、アプリが使用するボットトークンは同じになります。_ほとんど_のアプリで使用されるのは、ボットトークンです。 +- [アプリレベルトークン](/authentication/tokens#app-level) は、全ての組織(とその配下のワークスペースでの個々のユーザーによるインストール)を横断して、あなたのアプリを代理するものです。アプリレベルトークンは、アプリの WebSocket コネクションを確立するためによく使われます。 このガイドではボットトークンとアプリレベルトークンを使用します。 1. 左サイドバーの「**OAuth & Permissions**」をクリックし、「**Bot Token Scopes**」セクションまで下にスクロールします。「**Add an OAuth Scope**」をクリックします。 -2. ここでは [`chat:write`](https://docs.slack.dev/reference/scopes/chat.write) というスコープのみを追加します。このスコープはアプリが参加しているチャンネルにメッセージを投稿することを許可します。 +2. ここでは [`chat:write`](/reference/scopes/chat.write) というスコープのみを追加します。このスコープはアプリが参加しているチャンネルにメッセージを投稿することを許可します。 3. OAuth & Permissions ページの一番上までスクロールし、「**Install App to Workspace**」をクリックします。Slack の OAuth 確認画面 が表示されます。この画面で開発用ワークスペースへのアプリのインストールを承認します。 4. インストールを承認すると **OAuth & Permissions** ページが表示され、**Bot User OAuth Access Token** を確認できるでしょう。 -![OAuth トークン](/img/boltpy/bot-token.png "ボット用 OAuth トークン") +![OAuth トークン](/img/bolt-python/bot-token.png "ボット用 OAuth トークン") 5. 次に「**Basic Informationのページ**」まで戻り、アプリレベルトークンのセクションまで下にスクロールし「**Generate Token and Scopes**」をクリックしてアプリレベルトークンを作成します。このトークンに `connections:write` のスコープを付与し、作成された `xapp` トークンを保存します。これらのトークンは後ほど利用します。 6. 左サイドメニューの「**Socket Mode**」を有効にします。 -:::tip - -トークンはパスワードと同様に取り扱い、[安全な方法で保管してください](https://docs.slack.dev/authentication/best-practices-for-security)。アプリはこのトークンを使って Slack ワークスペースで投稿をしたり、情報の取得をしたりします。 +:::tip[トークンはパスワードと同様に取り扱い、[安全な方法で保管してください](/authentication/best-practices-for-security)。アプリはこのトークンを使って Slack ワークスペースで投稿をしたり、情報の取得をしたりします。] ::: @@ -99,9 +89,9 @@ export SLACK_BOT_TOKEN=xoxb-<ボットトークン> ```shell export SLACK_APP_TOKEN=<アプリレベルトークン> ``` -:::warning +:::warning[🔒 全てのトークンは安全に保管してください。] -🔒 全てのトークンは安全に保管してください。少なくともパブリックなバージョン管理にチェックインするようなことは避けるべきでしょう。また、上にあった例のように環境変数を介してアクセスするようにしてください。詳細な情報は [アプリのセキュリティのベストプラクティス](https://docs.slack.dev/authentication/best-practices-for-security)のドキュメントを参照してください。 +少なくともパブリックなバージョン管理にチェックインするようなことは避けるべきでしょう。また、上にあった例のように環境変数を介してアクセスするようにしてください。詳細な情報は [アプリのセキュリティのベストプラクティス](/authentication/best-practices-for-security)のドキュメントを参照してください。 ::: @@ -139,7 +129,7 @@ python3 app.py ### イベントを設定する {#setting-up-events} アプリはワークスペース内の他のメンバーと同じように振る舞い、メッセージを投稿したり、絵文字リアクションを追加したり、イベントをリッスンして返答したりできます。 -Slack ワークスペースで発生するイベント(メッセージが投稿されたときや、メッセージに対するリアクションがつけられたときなど)をリッスンするには、[Events API を使って特定の種類のイベントをサブスクライブします](https://docs.slack.dev/apis/events-api/)。 +Slack ワークスペースで発生するイベント(メッセージが投稿されたときや、メッセージに対するリアクションがつけられたときなど)をリッスンするには、[Events API を使って特定の種類のイベントをサブスクライブします](/apis/events-api/)。 このチュートリアルの序盤でソケットモードを有効にしました。ソケットモードを使うことで、アプリが公開された HTTP エンドポイントを公開せずに Events API やインタラクティブコンポーネントを利用できるようになります。このことは、開発時やファイヤーウォールの裏からのリクエストを受ける際に便利です。HTTP での方式は、ホスティング環境にデプロイするアプリや Slack App Directory で配布されるアプリの開発・運用に適しています。 @@ -164,11 +154,11 @@ import TabItem from '@theme/TabItem'; 1. アプリ構成ページに戻ります ([アプリ管理ページから](https://api.slack.com/apps) アプリをクリックします)。左側のサイドバーで [**イベント サブスクリプション**] をクリックします。 **イベントを有効にする**というラベルの付いたスイッチを切り替えます。 -2. リクエスト URL を追加します。 Slack は、イベントに対応する HTTP POST リクエストをこの [リクエスト URL](https://docs.slack.dev/apis/events-api/#subscribing) に送信します。 Bolt は、`/slack/events` パスを使用して、すべての受信リクエスト (ショートカット、イベント、対話性ペイロードなど) をリッスンします。アプリ構成内でリクエスト URL を構成する場合は、`/slack/events` を追加します。 「https://あなたのドメイン/slack/events」。 💡 Bolt アプリが実行されている限り、URL は検証されるはずです。 +2. リクエスト URL を追加します。 Slack は、イベントに対応する HTTP POST リクエストをこの [リクエスト URL](/apis/events-api/#subscribing) に送信します。 Bolt は、`/slack/events` パスを使用して、すべての受信リクエスト (ショートカット、イベント、対話性ペイロードなど) をリッスンします。アプリ構成内でリクエスト URL を構成する場合は、`/slack/events` を追加します。 「https://あなたのドメイン/slack/events」。 💡 Bolt アプリが実行されている限り、URL は検証されるはずです。 -:::tip +:::tip -ローカル開発の場合、ngrok などのプロキシ サービスを使用してパブリック URL を作成し、リクエストを開発環境にトンネリングできます。このトンネルの作成方法については、[ngrok のスタート ガイド](https://ngrok.com/docs#getting-started-expose) を参照してください。アプリをホスティングする際には、Slack 開発者がアプリをホストするために使用する最も一般的なホスティング プロバイダーを [API サイト](https://docs.slack.dev/distribution/hosting-slack-apps/) に集めました。 +ローカル開発の場合、ngrok などのプロキシ サービスを使用してパブリック URL を作成し、リクエストを開発環境にトンネリングできます。このトンネルの作成方法については、[ngrok のスタート ガイド](https://ngrok.com/docs#getting-started-expose) を参照してください。アプリをホスティングする際には、Slack 開発者がアプリをホストするために使用する最も一般的なホスティング プロバイダーを [API サイト](/app-management/hosting-slack-apps) に集めました。 ::: @@ -176,10 +166,10 @@ import TabItem from '@theme/TabItem'; 左側のサイドバーから **Event Subscriptions** にアクセスして、機能を有効にしてください。 **Subscribe to Bot Events** 配下で、ボットが受け取れるイベントを追加することができます。4つのメッセージに関するイベントがあります。 -- [`message.channels`](https://docs.slack.dev/reference/events/message.channels) アプリが参加しているパブリックチャンネルのメッセージをリッスン -- [`message.groups`](https://docs.slack.dev/reference/events/message.groups) アプリが参加しているプライベートチャンネルのメッセージをリッスン -- [`message.im`](https://docs.slack.dev/reference/events/message.im) あなたのアプリとユーザーのダイレクトメッセージをリッスン -- [`message.mpim`](https://docs.slack.dev/reference/events/message.mpim) あなたのアプリが追加されているグループ DM をリッスン +- [`message.channels`](/reference/events/message.channels) アプリが参加しているパブリックチャンネルのメッセージをリッスン +- [`message.groups`](/reference/events/message.groups) アプリが参加しているプライベートチャンネルのメッセージをリッスン +- [`message.im`](/reference/events/message.im) あなたのアプリとユーザーのダイレクトメッセージをリッスン +- [`message.mpim`](/reference/events/message.mpim) あなたのアプリが追加されているグループ DM をリッスン ボットが参加するすべての場所のメッセージをリッスンさせるには、これら 4 つのメッセージイベントをすべて選択します。ボットにリッスンさせるメッセージイベントの種類を選択したら、「**Save Changes**」ボタンをクリックします。 @@ -203,7 +193,7 @@ app = App(token=os.environ.get("SLACK_BOT_TOKEN")) # 'こんにちは' を含むメッセージをリッスンします # 指定可能なリスナーのメソッド引数の一覧は以下のモジュールドキュメントを参考にしてください: -# https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html @app.message("こんにちは") def message_hello(message, say): # イベントがトリガーされたチャンネルへ say() でメッセージを送信します @@ -229,7 +219,7 @@ app = App( # 'hello' を含むメッセージをリッスンします # 指定可能なリスナーのメソッド引数の一覧は以下のモジュールドキュメントを参考にしてください: -# https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html @app.message("hello") def message_hello(message, say): # イベントがトリガーされたチャンネルへ say() でメッセージを送信します @@ -359,9 +349,9 @@ if __name__ == "__main__": ボタンを含む `accessory` オブジェクトでは、`action_id` を指定していることがわかります。これは、ボタンを一意に示す識別子として機能します。これを使って、アプリをどのアクションに応答させるかを指定できます。 -:::tip +:::tip[[Block Kit Builder](https://app.slack.com/block-kit-builder) を使用すると、インタラクティブなメッセージのプロトタイプを簡単に作成できます。] -[Block Kit Builder](https://app.slack.com/block-kit-builder) を使用すると、インタラクティブなメッセージのプロトタイプを簡単に作成できます。自分自身やチームメンバーがメッセージのモックアップを作成し、生成される JSON をアプリに直接貼りつけることができます。 +自分自身やチームメンバーがメッセージのモックアップを作成し、生成される JSON をアプリに直接貼りつけることができます。 ::: @@ -468,6 +458,6 @@ if __name__ == "__main__": ここまでで基本的なアプリをセットアップして実行することはできたので、次は自分だけの Bolt アプリを作る方法について調べてみてください。参考になりそうなリソースをいくつかご紹介します。 * 基本的な概念について読んでみてください。Bolt アプリがアクセスできるさまざまメソッドや機能について知ることができます。 -* [`app.event()` メソッド](/concepts/event-listening)でボットがリッスンできるイベントをほかにも試してみましょう。すべてのイベントの一覧は [API サイト](https://docs.slack.dev/reference/events)で確認できます。 -* Bolt では、アプリにアタッチされたクライアントから [Web API メソッドを呼び出す](/concepts/web-api)ことができます。API サイトに [220 以上のメソッド](https://docs.slack.dev/reference/methods)を一覧しています。 -* [API サイト](https://docs.slack.dev/authentication/tokens)でほかのタイプのトークンを確認してみてください。アプリで実行したいアクションによって、異なるトークンが必要になる場合があります。 +* [`app.event()` メソッド](/tools/bolt-python/concepts/event-listening)でボットがリッスンできるイベントをほかにも試してみましょう。すべてのイベントの一覧は [API サイト](/reference/events)で確認できます。 +* Bolt では、アプリにアタッチされたクライアントから [Web API メソッドを呼び出す](/tools/bolt-python/concepts/web-api)ことができます。API サイトに [220 以上のメソッド](/reference/methods)を一覧しています。 +* [API サイト](/authentication/tokens)でほかのタイプのトークンを確認してみてください。アプリで実行したいアクションによって、異なるトークンが必要になる場合があります。 diff --git a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/legacy/steps-from-apps.md b/docs/japanese/legacy/steps-from-apps.md similarity index 71% rename from docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/legacy/steps-from-apps.md rename to docs/japanese/legacy/steps-from-apps.md index 554b2a1f4..a7ef2e04a 100644 --- a/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/legacy/steps-from-apps.md +++ b/docs/japanese/legacy/steps-from-apps.md @@ -1,10 +1,6 @@ ---- -title: ワークフローステップの概要 -lang: ja-jp -slug: /concepts/steps-from-apps ---- +# ワークフローステップの概要 -(アプリによる)ワークフローステップでは、処理をアプリ側で行うカスタムのワークフローステップを提供することができます。ユーザーは[ワークフロービルダー](https://docs.slack.dev/workflows/workflow-builder)を使ってこれらのステップをワークフローに追加できます。 +(アプリによる)ワークフローステップでは、処理をアプリ側で行うカスタムのワークフローステップを提供することができます。ユーザーは[ワークフロービルダー](/workflows/workflow-builder)を使ってこれらのステップをワークフローに追加できます。 ワークフローステップは、次の 3 つのユーザーイベントで構成されます。 @@ -14,7 +10,7 @@ slug: /concepts/steps-from-apps ワークフローステップを機能させるためには、これら 3 つのイベントすべてに対応する必要があります。 -アプリを使ったワークフローステップに関する詳細は、[API ドキュメント](https://docs.slack.dev/legacy/legacy-steps-from-apps/)を参照してください。 +アプリを使ったワークフローステップに関する詳細は、[API ドキュメント](/legacy/legacy-steps-from-apps/)を参照してください。 ## ステップの定義 @@ -26,9 +22,9 @@ slug: /concepts/steps-from-apps `WorkflowStep` のインスタンスを作成したら、それを`app.step()` メソッドに渡します。これによって、アプリがワークフローステップのイベントをリッスンし、設定オブジェクトで指定されたコールバックを使ってそれに応答できるようになります。 -また、デコレーターとして利用できる `WorkflowStepBuilder` クラスを使ってワークフローステップを定義することもできます。 詳細は、[こちらのドキュメント](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder)のコード例などを参考にしてください。 +また、デコレーターとして利用できる `WorkflowStepBuilder` クラスを使ってワークフローステップを定義することもできます。 詳細は、[こちらのドキュメント](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder)のコード例などを参考にしてください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python import os @@ -63,15 +59,15 @@ app.step(ws) ## ステップの追加・編集 -作成したワークフローステップがワークフローに追加またはその設定を変更されるタイミングで、[`workflow_step_edit` イベントがアプリに送信されます](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step_edit-payload)。このイベントがアプリに届くと、`WorkflowStep` で設定した `edit` コールバックが実行されます。 +作成したワークフローステップがワークフローに追加またはその設定を変更されるタイミングで、[`workflow_step_edit` イベントがアプリに送信されます](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step_edit-payload)。このイベントがアプリに届くと、`WorkflowStep` で設定した `edit` コールバックが実行されます。 -ステップの追加と編集のどちらが行われるときも、[ワークフローステップの設定モーダル](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object)をビルダーに送信する必要があります。このモーダルは、そのステップ独自の設定を選択するための場所です。通常のモーダルより制限が強く、例えば `title`、`submit`、`close` のプロパティを含めることができません。設定モーダルの `callback_id` は、デフォルトではワークフローステップと同じものになります。 +ステップの追加と編集のどちらが行われるときも、[ワークフローステップの設定モーダル](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object)をビルダーに送信する必要があります。このモーダルは、そのステップ独自の設定を選択するための場所です。通常のモーダルより制限が強く、例えば `title`、`submit`、`close` のプロパティを含めることができません。設定モーダルの `callback_id` は、デフォルトではワークフローステップと同じものになります。 `edit` コールバック内で `configure()` ユーティリティを使用すると、対応する `blocks` 引数にビューのblocks 部分だけを渡して、ステップの設定モーダルを簡単に表示させることができます。必要な入力内容が揃うまで設定の保存を無効にするには、`True` の値をセットした `submit_disabled` を渡します。 -設定モーダルの開き方に関する詳細は、[こちらのドキュメント](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object)を参照してください。 +設定モーダルの開き方に関する詳細は、[こちらのドキュメント](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object)を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python def edit(ack, step, configure): @@ -121,9 +117,9 @@ app.step(ws) - `step_name` : ステップのデフォルトの名前をオーバーライドします。 - `step_image_url` : ステップのデフォルトの画像をオーバーライドします。 -これらのパラメータの構成方法に関する詳細は、[こちらのドキュメント](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object)を参照してください。 +これらのパラメータの構成方法に関する詳細は、[こちらのドキュメント](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object)を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python def save(ack, view, update): @@ -162,13 +158,13 @@ app.step(ws) ## ステップの実行 -エンドユーザーがワークフローステップを実行すると、アプリに [`workflow_step_execute` イベントが送信されます](https://docs.slack.dev/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object)。このイベントがアプリに届くと、`WorkflowStep` で設定した `execute` コールバックが実行されます。 +エンドユーザーがワークフローステップを実行すると、アプリに [`workflow_step_execute` イベントが送信されます](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object)。このイベントがアプリに届くと、`WorkflowStep` で設定した `execute` コールバックが実行されます。 `save` コールバックで取り出した `inputs` を使って、サードパーティの API を呼び出す、情報をデータベースに保存する、ユーザーのホームタブを更新するといった処理を実行することができます。また、ワークフローの後続のステップで利用する出力値を `outputs` オブジェクトに設定します。 `execute` コールバック内では、`complete()` を呼び出してステップの実行が成功したことを示すか、`fail()` を呼び出してステップの実行が失敗したことを示す必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 ```python def execute(step, complete, fail): inputs = step["inputs"] diff --git a/docs/navbarConfig.js b/docs/navbarConfig.js deleted file mode 100644 index b243299f1..000000000 --- a/docs/navbarConfig.js +++ /dev/null @@ -1,97 +0,0 @@ -const navbar = { - style: 'dark', - title: 'Slack Developer Tools', - logo: { - src: 'img/slack-logo-on-white.png', - href: 'https://tools.slack.dev', - }, - items: [ - { - type: 'dropdown', - label: 'Bolt', - position: 'left', - items: [ - { - label: 'Java', - to: 'https://tools.slack.dev/java-slack-sdk/guides/bolt-basics', - target: '_self', - }, - { - label: 'JavaScript', - to: 'https://tools.slack.dev/bolt-js', - target: '_self', - }, - { - label: 'Python', - to: 'https://tools.slack.dev/bolt-python', - target: '_self', - }, - ], - }, - { - type: 'dropdown', - label: 'SDKs', - position: 'left', - items: [ - { - label: 'Java Slack SDK', - to: 'https://tools.slack.dev/java-slack-sdk/', - target: '_self', - }, - { - label: 'Node Slack SDK', - to: 'https://tools.slack.dev/node-slack-sdk/', - target: '_self', - }, - { - label: 'Python Slack SDK', - to: 'https://tools.slack.dev/python-slack-sdk/', - target: '_self', - }, - { - label: 'Deno Slack SDK', - to: 'https://tools.slack.dev/deno-slack-sdk/', - target: '_self', - }, - ], - }, - { - to: 'https://tools.slack.dev/slack-cli', - label: 'Slack CLI', - target: '_self', - }, - { - to: 'https://docs.slack.dev/', - label: 'API Docs', - position: 'right', - target: '_self', - }, - { - label: 'Developer Program', - position: 'right', - to: 'https://api.slack.com/developer-program', - target: '_blank', - rel: "noopener noreferrer" - }, - { - label: 'Your apps', - to: 'https://api.slack.com/apps', - position: 'right', - target: '_blank', - rel: "noopener noreferrer" - }, - { - type: 'localeDropdown', - position: 'right', - }, - { - 'aria-label': 'GitHub Repository', - className: 'navbar-github-link', - href: 'https://github.com/slackapi', - position: 'right', - target: '_self', - }, - ], -}; - -module.exports = navbar; diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index ec7f3558a..000000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,16738 +0,0 @@ -{ - "name": "website", - "version": "2024.08.01", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "website", - "version": "2024.08.01", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/plugin-client-redirects": "^3.8.1", - "@docusaurus/preset-classic": "3.8.1", - "@mdx-js/react": "^3.1.0", - "clsx": "^2.0.0", - "docusaurus-theme-github-codeblock": "^2.0.2", - "prism-react-renderer": "^2.4.1", - "react": "^19.1.1", - "react-dom": "^19.1.1" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/types": "3.8.1" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@algolia/autocomplete-core": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.9.tgz", - "integrity": "sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ==", - "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.17.9", - "@algolia/autocomplete-shared": "1.17.9" - } - }, - "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.9.tgz", - "integrity": "sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ==", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.9" - }, - "peerDependencies": { - "search-insights": ">= 1 < 3" - } - }, - "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.9.tgz", - "integrity": "sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ==", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.9" - }, - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/autocomplete-shared": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.9.tgz", - "integrity": "sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ==", - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/client-abtesting": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.30.0.tgz", - "integrity": "sha512-Q3OQXYlTNqVUN/V1qXX8VIzQbLjP3yrRBO9m6NRe1CBALmoGHh9JrYosEGvfior28+DjqqU3Q+nzCSuf/bX0Gw==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-analytics": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.30.0.tgz", - "integrity": "sha512-/b+SAfHjYjx/ZVeVReCKTTnFAiZWOyvYLrkYpeNMraMT6akYRR8eC1AvFcvR60GLG/jytxcJAp42G8nN5SdcLg==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-common": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.30.0.tgz", - "integrity": "sha512-tbUgvkp2d20mHPbM0+NPbLg6SzkUh0lADUUjzNCF+HiPkjFRaIW3NGMlESKw5ia4Oz6ZvFzyREquUX6rdkdJcQ==", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-insights": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.30.0.tgz", - "integrity": "sha512-caXuZqJK761m32KoEAEkjkE2WF/zYg1McuGesWXiLSgfxwZZIAf+DljpiSToBUXhoPesvjcLtINyYUzbkwE0iw==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-personalization": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.30.0.tgz", - "integrity": "sha512-7K6P7TRBHLX1zTmwKDrIeBSgUidmbj6u3UW/AfroLRDGf9oZFytPKU49wg28lz/yulPuHY0nZqiwbyAxq9V17w==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-query-suggestions": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.30.0.tgz", - "integrity": "sha512-WMjWuBjYxJheRt7Ec5BFr33k3cV0mq2WzmH9aBf5W4TT8kUp34x91VRsYVaWOBRlxIXI8o/WbhleqSngiuqjLA==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-search": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.30.0.tgz", - "integrity": "sha512-puc1/LREfSqzgmrOFMY5L/aWmhYOlJ0TTpa245C0ZNMKEkdOkcimFbXTXQ8lZhzh+rlyFgR7cQGNtXJ5H0XgZg==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/events": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" - }, - "node_modules/@algolia/ingestion": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.30.0.tgz", - "integrity": "sha512-NfqiIKVgGKTLr6T9F81oqB39pPiEtILTy0z8ujxPKg2rCvI/qQeDqDWFBmQPElCfUTU6kk67QAgMkQ7T6fE+gg==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/monitoring": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.30.0.tgz", - "integrity": "sha512-/eeM3aqLKro5KBZw0W30iIA6afkGa+bcpvEM0NDa92m5t3vil4LOmJI9FkgzfmSkF4368z/SZMOTPShYcaVXjA==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/recommend": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.30.0.tgz", - "integrity": "sha512-iWeAUWqw+xT+2IyUyTqnHCK+cyCKYV5+B6PXKdagc9GJJn6IaPs8vovwoC0Za5vKCje/aXQ24a2Z1pKpc/tdHg==", - "dependencies": { - "@algolia/client-common": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-browser-xhr": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.30.0.tgz", - "integrity": "sha512-alo3ly0tdNLjfMSPz9dmNwYUFHx7guaz5dTGlIzVGnOiwLgIoM6NgA+MJLMcH6e1S7OpmE2AxOy78svlhst2tQ==", - "dependencies": { - "@algolia/client-common": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-fetch": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.30.0.tgz", - "integrity": "sha512-WOnTYUIY2InllHBy6HHMpGIOo7Or4xhYUx/jkoSK/kPIa1BRoFEHqa8v4pbKHtoG7oLvM2UAsylSnjVpIhGZXg==", - "dependencies": { - "@algolia/client-common": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-node-http": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.30.0.tgz", - "integrity": "sha512-uSTUh9fxeHde1c7KhvZKUrivk90sdiDftC+rSKNFKKEU9TiIKAGA7B2oKC+AoMCqMymot1vW9SGbeESQPTZd0w==", - "dependencies": { - "@algolia/client-common": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz", - "integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.3", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", - "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", - "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", - "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", - "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-wrap-function": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", - "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", - "integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", - "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", - "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", - "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", - "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", - "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", - "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", - "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", - "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", - "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", - "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", - "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/template": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", - "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", - "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", - "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", - "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", - "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", - "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", - "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", - "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", - "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", - "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", - "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", - "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", - "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", - "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", - "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", - "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", - "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", - "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", - "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", - "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", - "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.27.1.tgz", - "integrity": "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", - "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", - "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", - "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", - "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", - "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", - "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", - "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.4.tgz", - "integrity": "sha512-D68nR5zxU64EUzV8i7T3R5XP0Xhrou/amNnddsRQssx6GrTLdZl1rLxyjtVZBd+v/NVX4AbTPOB5aU8thAZV1A==", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", - "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3", - "core-js-compat": "^3.40.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", - "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", - "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", - "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", - "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-syntax-typescript": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", - "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", - "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", - "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", - "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", - "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.25.9", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.25.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", - "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-transform-react-display-name": "^7.25.9", - "@babel/plugin-transform-react-jsx": "^7.25.9", - "@babel/plugin-transform-react-jsx-development": "^7.25.9", - "@babel/plugin-transform-react-pure-annotations": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", - "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", - "@babel/plugin-transform-typescript": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.6.tgz", - "integrity": "sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==", - "dependencies": { - "core-js-pure": "^3.30.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", - "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@csstools/cascade-layer-name-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", - "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", - "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz", - "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-cascade-layers/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-color-function": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.10.tgz", - "integrity": "sha512-4dY0NBu7NVIpzxZRgh/Q/0GPSz/jLSw0i/u3LTUor0BkQcz/fNhN10mSWBDsL0p9nDb0Ky1PD6/dcGbhACuFTQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-mix-function": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.10.tgz", - "integrity": "sha512-P0lIbQW9I4ShE7uBgZRib/lMTf9XMjJkFl/d6w4EMNHu2qvQ6zljJGEcBkw/NsBtq/6q3WrmgxSS8kHtPMkK4Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.0.tgz", - "integrity": "sha512-Z5WhouTyD74dPFPrVE7KydgNS9VvnjB8qcdes9ARpCOItb4jTnm7cHp4FhxCRUoyhabD0WVv43wbkJ4p8hLAlQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-content-alt-text": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.6.tgz", - "integrity": "sha512-eRjLbOjblXq+byyaedQRSrAejKGNAFued+LcbzT+LCL78fabxHkxYjBbxkroONxHHYu2qxhFK2dBStTLPG3jpQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-exponential-functions": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz", - "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-4.0.0.tgz", - "integrity": "sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-gamut-mapping": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.10.tgz", - "integrity": "sha512-QDGqhJlvFnDlaPAfCYPsnwVA6ze+8hhrwevYWlnUeSjkkZfBpcCO42SaUD8jiLlq7niouyLgvup5lh+f1qessg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.10.tgz", - "integrity": "sha512-HHPauB2k7Oits02tKFUeVFEU2ox/H3OQVrP3fSOKDxvloOikSal+3dzlyTZmYsb9FlY9p5EUpBtz0//XBmy+aw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.10.tgz", - "integrity": "sha512-nOKKfp14SWcdEQ++S9/4TgRKchooLZL0TUFdun3nI4KPwCjETmhjta1QT4ICQcGVWQTvrsgMM/aLB5We+kMHhQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.2.tgz", - "integrity": "sha512-lrK2jjyZwh7DbxaNnIUjkeDmU8Y6KyzRBk91ZkI5h8nb1ykEfZrtIVArdIjX4DHMIBGpdHrgP0n4qXDr7OHaKA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-initial": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz", - "integrity": "sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", - "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-light-dark-function": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.9.tgz", - "integrity": "sha512-1tCZH5bla0EAkFAI2r0H33CDnIBeLUaJh1p+hvvsylJ4svsv2wOmJjJn+OXwUZLXef37GYbRIVKX+X+g6m+3CQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-float-and-clear": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-3.0.0.tgz", - "integrity": "sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-overflow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overflow/-/postcss-logical-overflow-2.0.0.tgz", - "integrity": "sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-overscroll-behavior": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overscroll-behavior/-/postcss-logical-overscroll-behavior-2.0.0.tgz", - "integrity": "sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-resize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-3.0.0.tgz", - "integrity": "sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-viewport-units": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz", - "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-minmax": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz", - "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz", - "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-4.0.0.tgz", - "integrity": "sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.0.tgz", - "integrity": "sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.10.tgz", - "integrity": "sha512-ZzZUTDd0fgNdhv8UUjGCtObPD8LYxMH+MJsW9xlZaWTV8Ppr4PtxlHYNMmF4vVWGl0T6f8tyWAKjoI6vePSgAg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.1.0.tgz", - "integrity": "sha512-YrkI9dx8U4R8Sz2EJaoeD9fI7s7kmeEBfmO+UURNeL6lQI7VxF6sBE+rSqdCBn4onwqmxFdBU3lTwyYb/lCmxA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-random-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", - "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.10.tgz", - "integrity": "sha512-8+0kQbQGg9yYG8hv0dtEpOMLwB9M+P7PhacgIzVzJpixxV4Eq9AUQtQw8adMmAJU1RBBmIlpmtmm3XTRd/T00g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-scope-pseudo-class": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-4.0.1.tgz", - "integrity": "sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-sign-functions": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz", - "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz", - "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.2.tgz", - "integrity": "sha512-8XvCRrFNseBSAGxeaVTaNijAu+FzUvjwFXtcrynmazGb/9WUdsPCpBX+mHEHShVRq47Gy4peYAoxYs8ltUnmzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", - "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-unset-value": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz", - "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/utilities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", - "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docsearch/css": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.9.0.tgz", - "integrity": "sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA==" - }, - "node_modules/@docsearch/react": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.9.0.tgz", - "integrity": "sha512-mb5FOZYZIkRQ6s/NWnM98k879vu5pscWqTLubLFBO87igYYT4VzVazh4h5o/zCvTIZgEt3PvsCOMOswOUo9yHQ==", - "dependencies": { - "@algolia/autocomplete-core": "1.17.9", - "@algolia/autocomplete-preset-algolia": "1.17.9", - "@docsearch/css": "3.9.0", - "algoliasearch": "^5.14.2" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 20.0.0", - "react": ">= 16.8.0 < 20.0.0", - "react-dom": ">= 16.8.0 < 20.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } - } - }, - "node_modules/@docusaurus/babel": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.1.tgz", - "integrity": "sha512-3brkJrml8vUbn9aeoZUlJfsI/GqyFcDgQJwQkmBtclJgWDEQBKKeagZfOgx0WfUQhagL1sQLNW0iBdxnI863Uw==", - "dependencies": { - "@babel/core": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.25.9", - "@babel/preset-env": "^7.25.9", - "@babel/preset-react": "^7.25.9", - "@babel/preset-typescript": "^7.25.9", - "@babel/runtime": "^7.25.9", - "@babel/runtime-corejs3": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.8.1", - "@docusaurus/utils": "3.8.1", - "babel-plugin-dynamic-import-node": "^2.3.3", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/bundler": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.1.tgz", - "integrity": "sha512-/z4V0FRoQ0GuSLToNjOSGsk6m2lQUG4FRn8goOVoZSRsTrU8YR2aJacX5K3RG18EaX9b+52pN4m1sL3MQZVsQA==", - "dependencies": { - "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.8.1", - "@docusaurus/cssnano-preset": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "babel-loader": "^9.2.1", - "clean-css": "^5.3.3", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.11.0", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", - "file-loader": "^6.2.0", - "html-minifier-terser": "^7.2.0", - "mini-css-extract-plugin": "^2.9.2", - "null-loader": "^4.0.1", - "postcss": "^8.5.4", - "postcss-loader": "^7.3.4", - "postcss-preset-env": "^10.2.1", - "terser-webpack-plugin": "^5.3.9", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "webpack": "^5.95.0", - "webpackbar": "^6.0.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/faster": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/faster": { - "optional": true - } - } - }, - "node_modules/@docusaurus/core": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.1.tgz", - "integrity": "sha512-ENB01IyQSqI2FLtOzqSI3qxG2B/jP4gQPahl2C3XReiLebcVh5B5cB9KYFvdoOqOWPyr5gXK4sjgTKv7peXCrA==", - "dependencies": { - "@docusaurus/babel": "3.8.1", - "@docusaurus/bundler": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "boxen": "^6.2.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cli-table3": "^0.6.3", - "combine-promises": "^1.1.0", - "commander": "^5.1.0", - "core-js": "^3.31.1", - "detect-port": "^1.5.1", - "escape-html": "^1.0.3", - "eta": "^2.2.0", - "eval": "^0.1.8", - "execa": "5.1.1", - "fs-extra": "^11.1.1", - "html-tags": "^3.3.1", - "html-webpack-plugin": "^5.6.0", - "leven": "^3.1.0", - "lodash": "^4.17.21", - "open": "^8.4.0", - "p-map": "^4.0.0", - "prompts": "^2.4.2", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", - "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.3.4", - "react-router-config": "^5.1.1", - "react-router-dom": "^5.3.4", - "semver": "^7.5.4", - "serve-handler": "^6.1.6", - "tinypool": "^1.0.2", - "tslib": "^2.6.0", - "update-notifier": "^6.0.2", - "webpack": "^5.95.0", - "webpack-bundle-analyzer": "^4.10.2", - "webpack-dev-server": "^4.15.2", - "webpack-merge": "^6.0.1" - }, - "bin": { - "docusaurus": "bin/docusaurus.mjs" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@mdx-js/react": "^3.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/cssnano-preset": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.1.tgz", - "integrity": "sha512-G7WyR2N6SpyUotqhGznERBK+x84uyhfMQM2MmDLs88bw4Flom6TY46HzkRkSEzaP9j80MbTN8naiL1fR17WQug==", - "dependencies": { - "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.5.4", - "postcss-sort-media-queries": "^5.2.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/logger": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.1.tgz", - "integrity": "sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww==", - "dependencies": { - "chalk": "^4.1.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/mdx-loader": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.1.tgz", - "integrity": "sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w==", - "dependencies": { - "@docusaurus/logger": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", - "escape-html": "^1.0.3", - "estree-util-value-to-estree": "^3.0.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "image-size": "^2.0.2", - "mdast-util-mdx": "^3.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-raw": "^7.0.0", - "remark-directive": "^3.0.0", - "remark-emoji": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "stringify-object": "^3.3.0", - "tslib": "^2.6.0", - "unified": "^11.0.3", - "unist-util-visit": "^5.0.0", - "url-loader": "^4.1.1", - "vfile": "^6.0.1", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/module-type-aliases": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.1.tgz", - "integrity": "sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg==", - "dependencies": { - "@docusaurus/types": "3.8.1", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/@docusaurus/plugin-client-redirects": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.8.1.tgz", - "integrity": "sha512-F+86R7PBn6VNgy/Ux8w3ZRypJGJEzksbejQKlbTC8u6uhBUhfdXWkDp6qdOisIoW0buY5nLqucvZt1zNJzhJhA==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "eta": "^2.2.0", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.8.1.tgz", - "integrity": "sha512-vNTpMmlvNP9n3hGEcgPaXyvTljanAKIUkuG9URQ1DeuDup0OR7Ltvoc8yrmH+iMZJbcQGhUJF+WjHLwuk8HSdw==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "cheerio": "1.0.0-rc.12", - "feed": "^4.2.2", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "schema-dts": "^1.1.2", - "srcset": "^4.0.0", - "tslib": "^2.6.0", - "unist-util-visit": "^5.0.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/plugin-content-docs": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.1.tgz", - "integrity": "sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@types/react-router-config": "^5.0.7", - "combine-promises": "^1.1.0", - "fs-extra": "^11.1.1", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "schema-dts": "^1.1.2", - "tslib": "^2.6.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.8.1.tgz", - "integrity": "sha512-a+V6MS2cIu37E/m7nDJn3dcxpvXb6TvgdNI22vJX8iUTp8eoMoPa0VArEbWvCxMY/xdC26WzNv4wZ6y0iIni/w==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-css-cascade-layers": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.8.1.tgz", - "integrity": "sha512-VQ47xRxfNKjHS5ItzaVXpxeTm7/wJLFMOPo1BkmoMG4Cuz4nuI+Hs62+RMk1OqVog68Swz66xVPK8g9XTrBKRw==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/plugin-debug": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.8.1.tgz", - "integrity": "sha512-nT3lN7TV5bi5hKMB7FK8gCffFTBSsBsAfV84/v293qAmnHOyg1nr9okEw8AiwcO3bl9vije5nsUvP0aRl2lpaw==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "fs-extra": "^11.1.1", - "react-json-view-lite": "^2.3.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.8.1.tgz", - "integrity": "sha512-Hrb/PurOJsmwHAsfMDH6oVpahkEGsx7F8CWMjyP/dw1qjqmdS9rcV1nYCGlM8nOtD3Wk/eaThzUB5TSZsGz+7Q==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.8.1.tgz", - "integrity": "sha512-tKE8j1cEZCh8KZa4aa80zpSTxsC2/ZYqjx6AAfd8uA8VHZVw79+7OTEP2PoWi0uL5/1Is0LF5Vwxd+1fz5HlKg==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@types/gtag.js": "^0.0.12", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.8.1.tgz", - "integrity": "sha512-iqe3XKITBquZq+6UAXdb1vI0fPY5iIOitVjPQ581R1ZKpHr0qe+V6gVOrrcOHixPDD/BUKdYwkxFjpNiEN+vBw==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.8.1.tgz", - "integrity": "sha512-+9YV/7VLbGTq8qNkjiugIelmfUEVkTyLe6X8bWq7K5qPvGXAjno27QAfFq63mYfFFbJc7z+pudL63acprbqGzw==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "fs-extra": "^11.1.1", - "sitemap": "^7.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-svgr": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.8.1.tgz", - "integrity": "sha512-rW0LWMDsdlsgowVwqiMb/7tANDodpy1wWPwCcamvhY7OECReN3feoFwLjd/U4tKjNY3encj0AJSTxJA+Fpe+Gw==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@svgr/core": "8.1.0", - "@svgr/webpack": "^8.1.0", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/preset-classic": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.8.1.tgz", - "integrity": "sha512-yJSjYNHXD8POMGc2mKQuj3ApPrN+eG0rO1UPgSx7jySpYU+n4WjBikbrA2ue5ad9A7aouEtMWUoiSRXTH/g7KQ==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/plugin-content-blog": "3.8.1", - "@docusaurus/plugin-content-docs": "3.8.1", - "@docusaurus/plugin-content-pages": "3.8.1", - "@docusaurus/plugin-css-cascade-layers": "3.8.1", - "@docusaurus/plugin-debug": "3.8.1", - "@docusaurus/plugin-google-analytics": "3.8.1", - "@docusaurus/plugin-google-gtag": "3.8.1", - "@docusaurus/plugin-google-tag-manager": "3.8.1", - "@docusaurus/plugin-sitemap": "3.8.1", - "@docusaurus/plugin-svgr": "3.8.1", - "@docusaurus/theme-classic": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/theme-search-algolia": "3.8.1", - "@docusaurus/types": "3.8.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-classic": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.8.1.tgz", - "integrity": "sha512-bqDUCNqXeYypMCsE1VcTXSI1QuO4KXfx8Cvl6rYfY0bhhqN6d2WZlRkyLg/p6pm+DzvanqHOyYlqdPyP0iz+iw==", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/plugin-content-blog": "3.8.1", - "@docusaurus/plugin-content-docs": "3.8.1", - "@docusaurus/plugin-content-pages": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/theme-translations": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.45", - "lodash": "^4.17.21", - "nprogress": "^0.2.0", - "postcss": "^8.5.4", - "prism-react-renderer": "^2.3.0", - "prismjs": "^1.29.0", - "react-router-dom": "^5.3.4", - "rtlcss": "^4.1.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-common": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.1.tgz", - "integrity": "sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw==", - "dependencies": { - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "clsx": "^2.0.0", - "parse-numeric-range": "^1.3.0", - "prism-react-renderer": "^2.3.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/plugin-content-docs": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.1.tgz", - "integrity": "sha512-NBFH5rZVQRAQM087aYSRKQ9yGEK9eHd+xOxQjqNpxMiV85OhJDD4ZGz6YJIod26Fbooy54UWVdzNU0TFeUUUzQ==", - "dependencies": { - "@docsearch/react": "^3.9.0", - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/plugin-content-docs": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/theme-translations": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "algoliasearch": "^5.17.1", - "algoliasearch-helper": "^3.22.6", - "clsx": "^2.0.0", - "eta": "^2.2.0", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-translations": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.1.tgz", - "integrity": "sha512-OTp6eebuMcf2rJt4bqnvuwmm3NVXfzfYejL+u/Y1qwKhZPrjPoKWfk1CbOP5xH5ZOPkiAsx4dHdQBRJszK3z2g==", - "dependencies": { - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/types": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.1.tgz", - "integrity": "sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg==", - "dependencies": { - "@mdx-js/mdx": "^3.0.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "commander": "^5.1.0", - "joi": "^17.9.2", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "utility-types": "^3.10.0", - "webpack": "^5.95.0", - "webpack-merge": "^5.9.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/types/node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docusaurus/utils": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.1.tgz", - "integrity": "sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ==", - "dependencies": { - "@docusaurus/logger": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "escape-string-regexp": "^4.0.0", - "execa": "5.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "github-slugger": "^1.5.0", - "globby": "^11.1.0", - "gray-matter": "^4.0.3", - "jiti": "^1.20.0", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "micromatch": "^4.0.5", - "p-queue": "^6.6.2", - "prompts": "^2.4.2", - "resolve-pathname": "^3.0.0", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/utils-common": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.1.tgz", - "integrity": "sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg==", - "dependencies": { - "@docusaurus/types": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/utils-validation": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.1.tgz", - "integrity": "sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA==", - "dependencies": { - "@docusaurus/logger": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "fs-extra": "^11.2.0", - "joi": "^17.9.2", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "license": "MIT" - }, - "node_modules/@mdx-js/mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", - "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-to-js": "^2.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-estree": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "periscopic": "^3.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", - "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "license": "MIT", - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "license": "ISC" - }, - "node_modules/@pnpm/npm-conf": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", - "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", - "license": "MIT", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.28", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", - "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", - "license": "MIT" - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@slorber/remark-comment": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", - "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.1.0", - "micromark-util-symbol": "^1.0.1" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", - "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", - "dependencies": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/webpack": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", - "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", - "dependencies": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/acorn": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", - "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "license": "MIT", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/express/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/gtag.js": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", - "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "license": "MIT" - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "license": "MIT" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.16", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", - "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, - "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/prismjs": { - "version": "1.26.4", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz", - "integrity": "sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" - }, - "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-router": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", - "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "node_modules/@types/react-router-config": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.11.tgz", - "integrity": "sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "^5.1.0" - } - }, - "node_modules/@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "license": "MIT" - }, - "node_modules/@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" - }, - "node_modules/@types/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/algoliasearch": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.30.0.tgz", - "integrity": "sha512-ILSdPX4je0n5WUKD34TMe57/eqiXUzCIjAsdtLQYhomqOjTtFUg1s6dE7kUegc4Mc43Xr7IXYlMutU9HPiYfdw==", - "dependencies": { - "@algolia/client-abtesting": "5.30.0", - "@algolia/client-analytics": "5.30.0", - "@algolia/client-common": "5.30.0", - "@algolia/client-insights": "5.30.0", - "@algolia/client-personalization": "5.30.0", - "@algolia/client-query-suggestions": "5.30.0", - "@algolia/client-search": "5.30.0", - "@algolia/ingestion": "1.30.0", - "@algolia/monitoring": "1.30.0", - "@algolia/recommend": "5.30.0", - "@algolia/requester-browser-xhr": "5.30.0", - "@algolia/requester-fetch": "5.30.0", - "@algolia/requester-node-http": "5.30.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/algoliasearch-helper": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.26.0.tgz", - "integrity": "sha512-Rv2x3GXleQ3ygwhkhJubhhYGsICmShLAiqtUuJTUkr9uOCOXyF2E71LVT4XDnVffbknv8XgScP4U0Oxtgm+hIw==", - "dependencies": { - "@algolia/events": "^4.0.1" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 6" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/astring": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", - "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/babel-loader": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", - "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "license": "MIT" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/bonjour-service": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", - "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/boxen": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", - "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^6.2.0", - "chalk": "^4.1.2", - "cli-boxes": "^3.0.0", - "string-width": "^5.0.1", - "type-fest": "^2.5.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001720", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", - "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "license": "MIT" - }, - "node_modules/combine-promises": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", - "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compressible/node_modules/mime-db": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "license": "BSD-2-Clause", - "dependencies": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/copy-text-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", - "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/core-js": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", - "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", - "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.43.0.tgz", - "integrity": "sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "license": "MIT", - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/css-blank-pseudo": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz", - "integrity": "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-declaration-sorter": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", - "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.2.tgz", - "integrity": "sha512-nzol/h+E0bId46Kn2dQH5VElaknX2Sr0hFuB/1EomdC7j+OISt2ZzK7EHX9DZDY53WbIVAR7FYKSO2XnSf07MQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-has-pseudo/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", - "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "cssnano": "^6.0.1", - "jest-worker": "^29.4.3", - "postcss": "^8.4.24", - "schema-utils": "^4.0.1", - "serialize-javascript": "^6.0.1" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "@swc/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "lightningcss": { - "optional": true - } - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-10.0.0.tgz", - "integrity": "sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssdb": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.3.1.tgz", - "integrity": "sha512-XnDRQMXucLueX92yDe0LPKupXetWoFOgawr4O4X41l5TltgK2NVbJJVDnnOywDYfW1sTJ28AcXGKOqdRKwCcmQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - } - ] - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", - "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", - "dependencies": { - "cssnano-preset-default": "^6.1.2", - "lilconfig": "^3.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-advanced": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", - "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", - "dependencies": { - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.0", - "cssnano-preset-default": "^6.1.2", - "postcss-discard-unused": "^6.0.5", - "postcss-merge-idents": "^6.0.3", - "postcss-reduce-idents": "^6.0.3", - "postcss-zindex": "^6.0.2" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-default": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", - "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", - "dependencies": { - "browserslist": "^4.23.0", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^4.0.2", - "postcss-calc": "^9.0.1", - "postcss-colormin": "^6.1.0", - "postcss-convert-values": "^6.1.0", - "postcss-discard-comments": "^6.0.2", - "postcss-discard-duplicates": "^6.0.3", - "postcss-discard-empty": "^6.0.3", - "postcss-discard-overridden": "^6.0.2", - "postcss-merge-longhand": "^6.0.5", - "postcss-merge-rules": "^6.1.1", - "postcss-minify-font-values": "^6.1.0", - "postcss-minify-gradients": "^6.0.3", - "postcss-minify-params": "^6.1.0", - "postcss-minify-selectors": "^6.0.4", - "postcss-normalize-charset": "^6.0.2", - "postcss-normalize-display-values": "^6.0.2", - "postcss-normalize-positions": "^6.0.2", - "postcss-normalize-repeat-style": "^6.0.2", - "postcss-normalize-string": "^6.0.2", - "postcss-normalize-timing-functions": "^6.0.2", - "postcss-normalize-unicode": "^6.1.0", - "postcss-normalize-url": "^6.0.2", - "postcss-normalize-whitespace": "^6.0.2", - "postcss-ordered-values": "^6.0.2", - "postcss-reduce-initial": "^6.1.0", - "postcss-reduce-transforms": "^6.0.2", - "postcss-svgo": "^6.0.3", - "postcss-unique-selectors": "^6.0.4" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", - "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "license": "BSD-2-Clause", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT" - }, - "node_modules/detect-port": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", - "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", - "license": "MIT", - "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/docusaurus-theme-github-codeblock": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/docusaurus-theme-github-codeblock/-/docusaurus-theme-github-codeblock-2.0.2.tgz", - "integrity": "sha512-H2WoQPWOLjGZO6KS58Gsd+eUVjTFJemkReiSSu9chqokyLc/3Ih3+zPRYfuEZ/HsDvSMIarf7CNcp+Vt+/G+ig==", - "dependencies": { - "@docusaurus/types": "^3.0.0" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.161", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.161.tgz", - "integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", - "license": "MIT" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/emoticon": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", - "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-value-to-estree": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.3.3.tgz", - "integrity": "sha512-Db+m1WSD4+mUO7UgMeKkAwdbfNWwIxLt48XF2oFU9emPfXkIu+k5/nlOj313v7wqtAPo0f9REhUvznFrPkG8CQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", - "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "url": "https://github.com/eta-dev/eta?sponsor=1" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eval": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", - "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", - "dependencies": { - "@types/node": "*", - "require-like": ">= 0.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/express/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "license": "Apache-2.0", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "dependencies": { - "xml-js": "^1.6.11" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/file-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/file-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "license": "MIT", - "engines": { - "node": ">= 14.17" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "license": "ISC" - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "license": "MIT", - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/global-dirs/node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/property-information": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", - "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", - "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^0.4.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", - "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz", - "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==" - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz", - "integrity": "sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==", - "dependencies": { - "inline-style-parser": "0.2.3" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript/node_modules/property-information": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", - "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "license": "MIT" - }, - "node_modules/html-minifier-terser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", - "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "~5.3.2", - "commander": "^10.0.0", - "entities": "^4.4.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.15.1" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "engines": { - "node": ">=14" - } - }, - "node_modules/html-tags": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", - "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/html-webpack-plugin/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", - "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", - "license": "MIT" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", - "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", - "license": "MIT", - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/infima": { - "version": "0.2.0-alpha.45", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz", - "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "license": "MIT", - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", - "license": "MIT", - "dependencies": { - "package-json": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/launch-editor": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", - "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-directive": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", - "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", - "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", - "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.2.tgz", - "integrity": "sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-remove-position": "^5.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", - "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromark": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", - "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", - "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "license": "MIT", - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", - "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz", - "integrity": "sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==", - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", - "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-label": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", - "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz", - "integrity": "sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-space/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-title": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", - "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", - "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-character/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", - "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", - "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", - "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", - "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", - "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", - "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", - "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", - "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", - "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", - "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "license": "MIT", - "dependencies": { - "mime-db": "~1.33.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", - "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", - "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.6.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/null-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", - "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/null-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/null-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/null-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/null-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", - "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", - "license": "MIT", - "dependencies": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", - "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-numeric-range": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "license": "(WTFPL OR MIT)" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-to-regexp": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", - "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz", - "integrity": "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.10.tgz", - "integrity": "sha512-k9qX+aXHBiLTRrWoCJuUFI6F1iF6QJQUXNVWJVSbqZgj57jDhBlOvD8gNUGl35tgqDivbGLhZeW3Ongz4feuKA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-10.0.0.tgz", - "integrity": "sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-10.0.0.tgz", - "integrity": "sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-colormin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", - "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-convert-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", - "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-custom-media": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz", - "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-properties": { - "version": "14.0.6", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", - "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz", - "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz", - "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-discard-comments": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", - "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", - "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-empty": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", - "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", - "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-unused": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", - "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.2.tgz", - "integrity": "sha512-7qTqnL7nfLRyJK/AHSVrrXOuvDDzettC+wGoienURV8v2svNbu6zJC52ruZtHaO6mfcagFmuTGFdzRsJKB3k5Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-10.0.1.tgz", - "integrity": "sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-focus-within": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-9.0.1.tgz", - "integrity": "sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-6.0.0.tgz", - "integrity": "sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-image-set-function": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-7.0.0.tgz", - "integrity": "sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-lab-function": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.10.tgz", - "integrity": "sha512-tqs6TCEv9tC1Riq6fOzHuHcZyhg4k3gIAMB8GGY/zA1ssGdm6puHMVE7t75aOSoFg7UD2wyrFFhbldiCMyyFTQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-loader": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", - "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==", - "dependencies": { - "cosmiconfig": "^8.3.5", - "jiti": "^1.20.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-logical": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.1.0.tgz", - "integrity": "sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-merge-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", - "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", - "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-rules": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", - "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.2", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", - "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", - "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", - "dependencies": { - "colord": "^2.9.3", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-params": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", - "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", - "dependencies": { - "browserslist": "^4.23.0", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", - "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nesting": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", - "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/selector-resolve-nested": "^3.1.0", - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", - "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", - "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", - "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", - "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", - "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-string": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", - "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", - "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", - "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", - "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", - "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-opacity-percentage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-3.0.0.tgz", - "integrity": "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==", - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-ordered-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", - "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-6.0.0.tgz", - "integrity": "sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-10.0.0.tgz", - "integrity": "sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-preset-env": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.4.tgz", - "integrity": "sha512-q+lXgqmTMdB0Ty+EQ31SuodhdfZetUlwCA/F0zRcd/XdxjzI+Rl2JhZNz5US2n/7t9ePsvuhCnEN4Bmu86zXlA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/postcss-cascade-layers": "^5.0.2", - "@csstools/postcss-color-function": "^4.0.10", - "@csstools/postcss-color-mix-function": "^3.0.10", - "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.0", - "@csstools/postcss-content-alt-text": "^2.0.6", - "@csstools/postcss-exponential-functions": "^2.0.9", - "@csstools/postcss-font-format-keywords": "^4.0.0", - "@csstools/postcss-gamut-mapping": "^2.0.10", - "@csstools/postcss-gradients-interpolation-method": "^5.0.10", - "@csstools/postcss-hwb-function": "^4.0.10", - "@csstools/postcss-ic-unit": "^4.0.2", - "@csstools/postcss-initial": "^2.0.1", - "@csstools/postcss-is-pseudo-class": "^5.0.3", - "@csstools/postcss-light-dark-function": "^2.0.9", - "@csstools/postcss-logical-float-and-clear": "^3.0.0", - "@csstools/postcss-logical-overflow": "^2.0.0", - "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", - "@csstools/postcss-logical-resize": "^3.0.0", - "@csstools/postcss-logical-viewport-units": "^3.0.4", - "@csstools/postcss-media-minmax": "^2.0.9", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", - "@csstools/postcss-nested-calc": "^4.0.0", - "@csstools/postcss-normalize-display-values": "^4.0.0", - "@csstools/postcss-oklab-function": "^4.0.10", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/postcss-random-function": "^2.0.1", - "@csstools/postcss-relative-color-syntax": "^3.0.10", - "@csstools/postcss-scope-pseudo-class": "^4.0.1", - "@csstools/postcss-sign-functions": "^1.1.4", - "@csstools/postcss-stepped-value-functions": "^4.0.9", - "@csstools/postcss-text-decoration-shorthand": "^4.0.2", - "@csstools/postcss-trigonometric-functions": "^4.0.9", - "@csstools/postcss-unset-value": "^4.0.0", - "autoprefixer": "^10.4.21", - "browserslist": "^4.25.0", - "css-blank-pseudo": "^7.0.1", - "css-has-pseudo": "^7.0.2", - "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.3.0", - "postcss-attribute-case-insensitive": "^7.0.1", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^7.0.10", - "postcss-color-hex-alpha": "^10.0.0", - "postcss-color-rebeccapurple": "^10.0.0", - "postcss-custom-media": "^11.0.6", - "postcss-custom-properties": "^14.0.6", - "postcss-custom-selectors": "^8.0.5", - "postcss-dir-pseudo-class": "^9.0.1", - "postcss-double-position-gradients": "^6.0.2", - "postcss-focus-visible": "^10.0.1", - "postcss-focus-within": "^9.0.1", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^6.0.0", - "postcss-image-set-function": "^7.0.0", - "postcss-lab-function": "^7.0.10", - "postcss-logical": "^8.1.0", - "postcss-nesting": "^13.0.2", - "postcss-opacity-percentage": "^3.0.0", - "postcss-overflow-shorthand": "^6.0.0", - "postcss-page-break": "^3.0.4", - "postcss-place": "^10.0.0", - "postcss-pseudo-class-any-link": "^10.0.1", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^8.0.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz", - "integrity": "sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-reduce-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", - "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", - "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", - "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-8.0.1.tgz", - "integrity": "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-sort-media-queries": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", - "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", - "dependencies": { - "sort-css-media-queries": "2.2.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.4.23" - } - }, - "node_modules/postcss-svgo": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", - "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" - }, - "engines": { - "node": "^14 || ^16 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", - "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/postcss-zindex": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", - "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/prism-react-renderer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", - "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", - "dependencies": { - "@types/prismjs": "^1.26.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "license": "ISC" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "license": "MIT", - "dependencies": { - "escape-goat": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", - "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", - "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.1" - } - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", - "license": "MIT" - }, - "node_modules/react-helmet-async": { - "name": "@slorber/react-helmet-async", - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz", - "integrity": "sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A==", - "license": "Apache-2.0", - "dependencies": { - "@babel/runtime": "^7.12.5", - "invariant": "^2.2.4", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.2.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-json-view-lite": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.4.1.tgz", - "integrity": "sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA==", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-loadable": { - "name": "@docusaurus/react-loadable", - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", - "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", - "dependencies": { - "@types/react": "*" - }, - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-loadable-ssr-addon-v5-slorber": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", - "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.3" - }, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "react-loadable": "*", - "webpack": ">=4.41.1 || 5.x" - } - }, - "node_modules/react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-config": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", - "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.1.2" - }, - "peerDependencies": { - "react": ">=15", - "react-router": ">=5" - } - }, - "node_modules/react-router-dom": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", - "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.4", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/registry-auth-token": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", - "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", - "license": "MIT", - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/registry-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "license": "MIT", - "dependencies": { - "rc": "1.2.8" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remark-directive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", - "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-emoji": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", - "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.2", - "emoticon": "^4.0.1", - "mdast-util-find-and-replace": "^3.0.1", - "node-emoji": "^2.1.0", - "unified": "^11.0.4" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/remark-frontmatter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", - "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-frontmatter": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", - "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/renderkid/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", - "engines": { - "node": "*" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rtlcss": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", - "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0", - "postcss": "^8.4.21", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "rtlcss": "bin/rtlcss.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/schema-dts": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz", - "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==" - }, - "node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/search-insights": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", - "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", - "peer": true - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "license": "MIT" - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "license": "MIT", - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/send/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-handler": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", - "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", - "license": "MIT", - "dependencies": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "mime-types": "2.1.18", - "minimatch": "3.1.2", - "path-is-inside": "1.0.2", - "path-to-regexp": "3.3.0", - "range-parser": "1.2.0" - } - }, - "node_modules/serve-handler/node_modules/path-to-regexp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", - "license": "MIT" - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "license": "ISC" - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "license": "ISC" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "node_modules/sitemap": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", - "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", - "dependencies": { - "@types/node": "^17.0.5", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" - }, - "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=12.0.0", - "npm": ">=5.6.0" - } - }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" - }, - "node_modules/skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "license": "MIT", - "dependencies": { - "unicode-emoji-modifier-base": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "license": "MIT", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sort-css-media-queries": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", - "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", - "engines": { - "node": ">= 6.3.0" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/srcset": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", - "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "license": "BSD-2-Clause", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-to-object": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", - "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", - "dependencies": { - "inline-style-parser": "0.1.1" - } - }, - "node_modules/stylehacks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", - "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz", - "integrity": "sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "license": "MIT" - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "node_modules/tinypool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", - "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" - }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "license": "MIT", - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", - "license": "BSD-2-Clause", - "dependencies": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", - "configstore": "^6.0.0", - "has-yarn": "^3.0.0", - "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.3.7", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/boxen": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", - "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "dependencies": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "file-loader": "*", - "webpack": "^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "file-loader": { - "optional": true - } - } - }, - "node_modules/url-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/url-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/url-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/url-loader/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/url-loader/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "license": "MIT" - }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", - "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webpack": { - "version": "5.96.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", - "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-bundle-analyzer": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", - "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "0.5.7", - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "commander": "^7.2.0", - "debounce": "^1.2.1", - "escape-string-regexp": "^4.0.0", - "gzip-size": "^6.0.0", - "html-escaper": "^2.0.2", - "opener": "^1.5.2", - "picocolors": "^1.0.0", - "sirv": "^2.0.3", - "ws": "^7.3.1" - }, - "bin": { - "webpack-bundle-analyzer": "lib/bin/analyzer.js" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "license": "MIT", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.15.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", - "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.4", - "ws": "^8.13.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/webpack/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpackbar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", - "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", - "dependencies": { - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "consola": "^3.2.3", - "figures": "^3.2.0", - "markdown-table": "^2.0.0", - "pretty-time": "^1.1.0", - "std-env": "^3.7.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=14.21.3" - }, - "peerDependencies": { - "webpack": "3 || 4 || 5" - } - }, - "node_modules/webpackbar/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/webpackbar/node_modules/markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "dependencies": { - "repeat-string": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webpackbar/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpackbar/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "license": "MIT", - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index b67d8a7a4..000000000 --- a/docs/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "website", - "version": "2024.08.01", - "private": true, - "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", - "clear": "docusaurus clear", - "serve": "docusaurus serve", - "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids" - }, - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/plugin-client-redirects": "^3.8.1", - "@docusaurus/preset-classic": "3.8.1", - "@mdx-js/react": "^3.1.0", - "clsx": "^2.0.0", - "docusaurus-theme-github-codeblock": "^2.0.2", - "prism-react-renderer": "^2.4.1", - "react": "^19.1.1", - "react-dom": "^19.1.1" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/types": "3.8.1" - }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 3 chrome version", - "last 3 firefox version", - "last 5 safari version" - ] - }, - "engines": { - "node": ">=20.0" - } -} diff --git a/docs/static/api-docs/slack_bolt/adapter/aiohttp/index.html b/docs/reference/adapter/aiohttp/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aiohttp/index.html rename to docs/reference/adapter/aiohttp/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html b/docs/reference/adapter/asgi/aiohttp/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/aiohttp/index.html rename to docs/reference/adapter/asgi/aiohttp/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html b/docs/reference/adapter/asgi/async_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/async_handler.html rename to docs/reference/adapter/asgi/async_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html b/docs/reference/adapter/asgi/base_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/base_handler.html rename to docs/reference/adapter/asgi/base_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html b/docs/reference/adapter/asgi/builtin/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/builtin/index.html rename to docs/reference/adapter/asgi/builtin/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html b/docs/reference/adapter/asgi/http_request.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/http_request.html rename to docs/reference/adapter/asgi/http_request.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/http_response.html b/docs/reference/adapter/asgi/http_response.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/http_response.html rename to docs/reference/adapter/asgi/http_response.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/index.html b/docs/reference/adapter/asgi/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/index.html rename to docs/reference/adapter/asgi/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/asgi/utils.html b/docs/reference/adapter/asgi/utils.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/asgi/utils.html rename to docs/reference/adapter/asgi/utils.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html b/docs/reference/adapter/aws_lambda/chalice_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_handler.html rename to docs/reference/adapter/aws_lambda/chalice_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html b/docs/reference/adapter/aws_lambda/chalice_lazy_listener_runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.html rename to docs/reference/adapter/aws_lambda/chalice_lazy_listener_runner.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html b/docs/reference/adapter/aws_lambda/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/handler.html rename to docs/reference/adapter/aws_lambda/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html b/docs/reference/adapter/aws_lambda/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/index.html rename to docs/reference/adapter/aws_lambda/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/internals.html b/docs/reference/adapter/aws_lambda/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/internals.html rename to docs/reference/adapter/aws_lambda/internals.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html b/docs/reference/adapter/aws_lambda/lambda_s3_oauth_flow.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.html rename to docs/reference/adapter/aws_lambda/lambda_s3_oauth_flow.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html b/docs/reference/adapter/aws_lambda/lazy_listener_runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/lazy_listener_runner.html rename to docs/reference/adapter/aws_lambda/lazy_listener_runner.html diff --git a/docs/static/api-docs/slack_bolt/adapter/aws_lambda/local_lambda_client.html b/docs/reference/adapter/aws_lambda/local_lambda_client.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/aws_lambda/local_lambda_client.html rename to docs/reference/adapter/aws_lambda/local_lambda_client.html diff --git a/docs/static/api-docs/slack_bolt/adapter/bottle/handler.html b/docs/reference/adapter/bottle/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/bottle/handler.html rename to docs/reference/adapter/bottle/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/bottle/index.html b/docs/reference/adapter/bottle/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/bottle/index.html rename to docs/reference/adapter/bottle/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html b/docs/reference/adapter/cherrypy/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/cherrypy/handler.html rename to docs/reference/adapter/cherrypy/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html b/docs/reference/adapter/cherrypy/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/cherrypy/index.html rename to docs/reference/adapter/cherrypy/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/django/handler.html b/docs/reference/adapter/django/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/django/handler.html rename to docs/reference/adapter/django/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/django/index.html b/docs/reference/adapter/django/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/django/index.html rename to docs/reference/adapter/django/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html b/docs/reference/adapter/falcon/async_resource.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/falcon/async_resource.html rename to docs/reference/adapter/falcon/async_resource.html diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/index.html b/docs/reference/adapter/falcon/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/falcon/index.html rename to docs/reference/adapter/falcon/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/falcon/resource.html b/docs/reference/adapter/falcon/resource.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/falcon/resource.html rename to docs/reference/adapter/falcon/resource.html diff --git a/docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html b/docs/reference/adapter/fastapi/async_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/fastapi/async_handler.html rename to docs/reference/adapter/fastapi/async_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/fastapi/index.html b/docs/reference/adapter/fastapi/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/fastapi/index.html rename to docs/reference/adapter/fastapi/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/flask/handler.html b/docs/reference/adapter/flask/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/flask/handler.html rename to docs/reference/adapter/flask/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/flask/index.html b/docs/reference/adapter/flask/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/flask/index.html rename to docs/reference/adapter/flask/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html b/docs/reference/adapter/google_cloud_functions/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/handler.html rename to docs/reference/adapter/google_cloud_functions/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html b/docs/reference/adapter/google_cloud_functions/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/google_cloud_functions/index.html rename to docs/reference/adapter/google_cloud_functions/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/index.html b/docs/reference/adapter/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/index.html rename to docs/reference/adapter/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html b/docs/reference/adapter/pyramid/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/pyramid/handler.html rename to docs/reference/adapter/pyramid/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/pyramid/index.html b/docs/reference/adapter/pyramid/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/pyramid/index.html rename to docs/reference/adapter/pyramid/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html b/docs/reference/adapter/sanic/async_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/sanic/async_handler.html rename to docs/reference/adapter/sanic/async_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/sanic/index.html b/docs/reference/adapter/sanic/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/sanic/index.html rename to docs/reference/adapter/sanic/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html b/docs/reference/adapter/socket_mode/aiohttp/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/aiohttp/index.html rename to docs/reference/adapter/socket_mode/aiohttp/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html b/docs/reference/adapter/socket_mode/async_base_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/async_base_handler.html rename to docs/reference/adapter/socket_mode/async_base_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html b/docs/reference/adapter/socket_mode/async_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/async_handler.html rename to docs/reference/adapter/socket_mode/async_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/async_internals.html b/docs/reference/adapter/socket_mode/async_internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/async_internals.html rename to docs/reference/adapter/socket_mode/async_internals.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html b/docs/reference/adapter/socket_mode/base_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/base_handler.html rename to docs/reference/adapter/socket_mode/base_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html b/docs/reference/adapter/socket_mode/builtin/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/builtin/index.html rename to docs/reference/adapter/socket_mode/builtin/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html b/docs/reference/adapter/socket_mode/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/index.html rename to docs/reference/adapter/socket_mode/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/internals.html b/docs/reference/adapter/socket_mode/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/internals.html rename to docs/reference/adapter/socket_mode/internals.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html b/docs/reference/adapter/socket_mode/websocket_client/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/websocket_client/index.html rename to docs/reference/adapter/socket_mode/websocket_client/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html b/docs/reference/adapter/socket_mode/websockets/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/socket_mode/websockets/index.html rename to docs/reference/adapter/socket_mode/websockets/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html b/docs/reference/adapter/starlette/async_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/starlette/async_handler.html rename to docs/reference/adapter/starlette/async_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/handler.html b/docs/reference/adapter/starlette/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/starlette/handler.html rename to docs/reference/adapter/starlette/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/starlette/index.html b/docs/reference/adapter/starlette/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/starlette/index.html rename to docs/reference/adapter/starlette/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html b/docs/reference/adapter/tornado/async_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/tornado/async_handler.html rename to docs/reference/adapter/tornado/async_handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/handler.html b/docs/reference/adapter/tornado/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/tornado/handler.html rename to docs/reference/adapter/tornado/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/tornado/index.html b/docs/reference/adapter/tornado/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/tornado/index.html rename to docs/reference/adapter/tornado/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html b/docs/reference/adapter/wsgi/handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/wsgi/handler.html rename to docs/reference/adapter/wsgi/handler.html diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html b/docs/reference/adapter/wsgi/http_request.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/wsgi/http_request.html rename to docs/reference/adapter/wsgi/http_request.html diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/http_response.html b/docs/reference/adapter/wsgi/http_response.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/wsgi/http_response.html rename to docs/reference/adapter/wsgi/http_response.html diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/index.html b/docs/reference/adapter/wsgi/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/wsgi/index.html rename to docs/reference/adapter/wsgi/index.html diff --git a/docs/static/api-docs/slack_bolt/adapter/wsgi/internals.html b/docs/reference/adapter/wsgi/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/adapter/wsgi/internals.html rename to docs/reference/adapter/wsgi/internals.html diff --git a/docs/static/api-docs/slack_bolt/app/app.html b/docs/reference/app/app.html similarity index 100% rename from docs/static/api-docs/slack_bolt/app/app.html rename to docs/reference/app/app.html diff --git a/docs/static/api-docs/slack_bolt/app/async_app.html b/docs/reference/app/async_app.html similarity index 100% rename from docs/static/api-docs/slack_bolt/app/async_app.html rename to docs/reference/app/async_app.html diff --git a/docs/static/api-docs/slack_bolt/app/async_server.html b/docs/reference/app/async_server.html similarity index 100% rename from docs/static/api-docs/slack_bolt/app/async_server.html rename to docs/reference/app/async_server.html diff --git a/docs/static/api-docs/slack_bolt/app/index.html b/docs/reference/app/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/app/index.html rename to docs/reference/app/index.html diff --git a/docs/static/api-docs/slack_bolt/async_app.html b/docs/reference/async_app.html similarity index 100% rename from docs/static/api-docs/slack_bolt/async_app.html rename to docs/reference/async_app.html diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize.html b/docs/reference/authorization/async_authorize.html similarity index 100% rename from docs/static/api-docs/slack_bolt/authorization/async_authorize.html rename to docs/reference/authorization/async_authorize.html diff --git a/docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html b/docs/reference/authorization/async_authorize_args.html similarity index 100% rename from docs/static/api-docs/slack_bolt/authorization/async_authorize_args.html rename to docs/reference/authorization/async_authorize_args.html diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize.html b/docs/reference/authorization/authorize.html similarity index 100% rename from docs/static/api-docs/slack_bolt/authorization/authorize.html rename to docs/reference/authorization/authorize.html diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize_args.html b/docs/reference/authorization/authorize_args.html similarity index 100% rename from docs/static/api-docs/slack_bolt/authorization/authorize_args.html rename to docs/reference/authorization/authorize_args.html diff --git a/docs/static/api-docs/slack_bolt/authorization/authorize_result.html b/docs/reference/authorization/authorize_result.html similarity index 100% rename from docs/static/api-docs/slack_bolt/authorization/authorize_result.html rename to docs/reference/authorization/authorize_result.html diff --git a/docs/static/api-docs/slack_bolt/authorization/index.html b/docs/reference/authorization/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/authorization/index.html rename to docs/reference/authorization/index.html diff --git a/docs/static/api-docs/slack_bolt/context/ack/ack.html b/docs/reference/context/ack/ack.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/ack/ack.html rename to docs/reference/context/ack/ack.html diff --git a/docs/static/api-docs/slack_bolt/context/ack/async_ack.html b/docs/reference/context/ack/async_ack.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/ack/async_ack.html rename to docs/reference/context/ack/async_ack.html diff --git a/docs/static/api-docs/slack_bolt/context/ack/index.html b/docs/reference/context/ack/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/ack/index.html rename to docs/reference/context/ack/index.html diff --git a/docs/static/api-docs/slack_bolt/context/ack/internals.html b/docs/reference/context/ack/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/ack/internals.html rename to docs/reference/context/ack/internals.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html b/docs/reference/context/assistant/assistant_utilities.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/assistant_utilities.html rename to docs/reference/context/assistant/assistant_utilities.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html b/docs/reference/context/assistant/async_assistant_utilities.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/async_assistant_utilities.html rename to docs/reference/context/assistant/async_assistant_utilities.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/index.html b/docs/reference/context/assistant/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/index.html rename to docs/reference/context/assistant/index.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/internals.html b/docs/reference/context/assistant/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/internals.html rename to docs/reference/context/assistant/internals.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html b/docs/reference/context/assistant/thread_context/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/thread_context/index.html rename to docs/reference/context/assistant/thread_context/index.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html b/docs/reference/context/assistant/thread_context_store/async_store.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/async_store.html rename to docs/reference/context/assistant/thread_context_store/async_store.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html b/docs/reference/context/assistant/thread_context_store/default_async_store.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_async_store.html rename to docs/reference/context/assistant/thread_context_store/default_async_store.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html b/docs/reference/context/assistant/thread_context_store/default_store.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/default_store.html rename to docs/reference/context/assistant/thread_context_store/default_store.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html b/docs/reference/context/assistant/thread_context_store/file/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/file/index.html rename to docs/reference/context/assistant/thread_context_store/file/index.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html b/docs/reference/context/assistant/thread_context_store/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/index.html rename to docs/reference/context/assistant/thread_context_store/index.html diff --git a/docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html b/docs/reference/context/assistant/thread_context_store/store.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/assistant/thread_context_store/store.html rename to docs/reference/context/assistant/thread_context_store/store.html diff --git a/docs/static/api-docs/slack_bolt/context/async_context.html b/docs/reference/context/async_context.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/async_context.html rename to docs/reference/context/async_context.html diff --git a/docs/static/api-docs/slack_bolt/context/base_context.html b/docs/reference/context/base_context.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/base_context.html rename to docs/reference/context/base_context.html diff --git a/docs/static/api-docs/slack_bolt/context/complete/async_complete.html b/docs/reference/context/complete/async_complete.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/complete/async_complete.html rename to docs/reference/context/complete/async_complete.html diff --git a/docs/static/api-docs/slack_bolt/context/complete/complete.html b/docs/reference/context/complete/complete.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/complete/complete.html rename to docs/reference/context/complete/complete.html diff --git a/docs/static/api-docs/slack_bolt/context/complete/index.html b/docs/reference/context/complete/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/complete/index.html rename to docs/reference/context/complete/index.html diff --git a/docs/static/api-docs/slack_bolt/context/context.html b/docs/reference/context/context.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/context.html rename to docs/reference/context/context.html diff --git a/docs/static/api-docs/slack_bolt/context/fail/async_fail.html b/docs/reference/context/fail/async_fail.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/fail/async_fail.html rename to docs/reference/context/fail/async_fail.html diff --git a/docs/static/api-docs/slack_bolt/context/fail/fail.html b/docs/reference/context/fail/fail.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/fail/fail.html rename to docs/reference/context/fail/fail.html diff --git a/docs/static/api-docs/slack_bolt/context/fail/index.html b/docs/reference/context/fail/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/fail/index.html rename to docs/reference/context/fail/index.html diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html b/docs/reference/context/get_thread_context/async_get_thread_context.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/get_thread_context/async_get_thread_context.html rename to docs/reference/context/get_thread_context/async_get_thread_context.html diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html b/docs/reference/context/get_thread_context/get_thread_context.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/get_thread_context/get_thread_context.html rename to docs/reference/context/get_thread_context/get_thread_context.html diff --git a/docs/static/api-docs/slack_bolt/context/get_thread_context/index.html b/docs/reference/context/get_thread_context/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/get_thread_context/index.html rename to docs/reference/context/get_thread_context/index.html diff --git a/docs/static/api-docs/slack_bolt/context/index.html b/docs/reference/context/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/index.html rename to docs/reference/context/index.html diff --git a/docs/static/api-docs/slack_bolt/context/respond/async_respond.html b/docs/reference/context/respond/async_respond.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/respond/async_respond.html rename to docs/reference/context/respond/async_respond.html diff --git a/docs/static/api-docs/slack_bolt/context/respond/index.html b/docs/reference/context/respond/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/respond/index.html rename to docs/reference/context/respond/index.html diff --git a/docs/static/api-docs/slack_bolt/context/respond/internals.html b/docs/reference/context/respond/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/respond/internals.html rename to docs/reference/context/respond/internals.html diff --git a/docs/static/api-docs/slack_bolt/context/respond/respond.html b/docs/reference/context/respond/respond.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/respond/respond.html rename to docs/reference/context/respond/respond.html diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html b/docs/reference/context/save_thread_context/async_save_thread_context.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/save_thread_context/async_save_thread_context.html rename to docs/reference/context/save_thread_context/async_save_thread_context.html diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/index.html b/docs/reference/context/save_thread_context/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/save_thread_context/index.html rename to docs/reference/context/save_thread_context/index.html diff --git a/docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html b/docs/reference/context/save_thread_context/save_thread_context.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/save_thread_context/save_thread_context.html rename to docs/reference/context/save_thread_context/save_thread_context.html diff --git a/docs/static/api-docs/slack_bolt/context/say/async_say.html b/docs/reference/context/say/async_say.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/say/async_say.html rename to docs/reference/context/say/async_say.html diff --git a/docs/static/api-docs/slack_bolt/context/say/index.html b/docs/reference/context/say/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/say/index.html rename to docs/reference/context/say/index.html diff --git a/docs/static/api-docs/slack_bolt/context/say/internals.html b/docs/reference/context/say/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/say/internals.html rename to docs/reference/context/say/internals.html diff --git a/docs/static/api-docs/slack_bolt/context/say/say.html b/docs/reference/context/say/say.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/say/say.html rename to docs/reference/context/say/say.html diff --git a/docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html b/docs/reference/context/set_status/async_set_status.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_status/async_set_status.html rename to docs/reference/context/set_status/async_set_status.html diff --git a/docs/static/api-docs/slack_bolt/context/set_status/index.html b/docs/reference/context/set_status/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_status/index.html rename to docs/reference/context/set_status/index.html diff --git a/docs/static/api-docs/slack_bolt/context/set_status/set_status.html b/docs/reference/context/set_status/set_status.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_status/set_status.html rename to docs/reference/context/set_status/set_status.html diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.html rename to docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html b/docs/reference/context/set_suggested_prompts/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_suggested_prompts/index.html rename to docs/reference/context/set_suggested_prompts/index.html diff --git a/docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.html rename to docs/reference/context/set_suggested_prompts/set_suggested_prompts.html diff --git a/docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html b/docs/reference/context/set_title/async_set_title.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_title/async_set_title.html rename to docs/reference/context/set_title/async_set_title.html diff --git a/docs/static/api-docs/slack_bolt/context/set_title/index.html b/docs/reference/context/set_title/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_title/index.html rename to docs/reference/context/set_title/index.html diff --git a/docs/static/api-docs/slack_bolt/context/set_title/set_title.html b/docs/reference/context/set_title/set_title.html similarity index 100% rename from docs/static/api-docs/slack_bolt/context/set_title/set_title.html rename to docs/reference/context/set_title/set_title.html diff --git a/docs/static/api-docs/slack_bolt/error/index.html b/docs/reference/error/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/error/index.html rename to docs/reference/error/index.html diff --git a/docs/static/api-docs/slack_bolt/index.html b/docs/reference/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/index.html rename to docs/reference/index.html diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/args.html b/docs/reference/kwargs_injection/args.html similarity index 100% rename from docs/static/api-docs/slack_bolt/kwargs_injection/args.html rename to docs/reference/kwargs_injection/args.html diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html b/docs/reference/kwargs_injection/async_args.html similarity index 100% rename from docs/static/api-docs/slack_bolt/kwargs_injection/async_args.html rename to docs/reference/kwargs_injection/async_args.html diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html b/docs/reference/kwargs_injection/async_utils.html similarity index 100% rename from docs/static/api-docs/slack_bolt/kwargs_injection/async_utils.html rename to docs/reference/kwargs_injection/async_utils.html diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/index.html b/docs/reference/kwargs_injection/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/kwargs_injection/index.html rename to docs/reference/kwargs_injection/index.html diff --git a/docs/static/api-docs/slack_bolt/kwargs_injection/utils.html b/docs/reference/kwargs_injection/utils.html similarity index 100% rename from docs/static/api-docs/slack_bolt/kwargs_injection/utils.html rename to docs/reference/kwargs_injection/utils.html diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/async_internals.html b/docs/reference/lazy_listener/async_internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/lazy_listener/async_internals.html rename to docs/reference/lazy_listener/async_internals.html diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html b/docs/reference/lazy_listener/async_runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/lazy_listener/async_runner.html rename to docs/reference/lazy_listener/async_runner.html diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/asyncio_runner.html b/docs/reference/lazy_listener/asyncio_runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/lazy_listener/asyncio_runner.html rename to docs/reference/lazy_listener/asyncio_runner.html diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/index.html b/docs/reference/lazy_listener/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/lazy_listener/index.html rename to docs/reference/lazy_listener/index.html diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/internals.html b/docs/reference/lazy_listener/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/lazy_listener/internals.html rename to docs/reference/lazy_listener/internals.html diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/runner.html b/docs/reference/lazy_listener/runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/lazy_listener/runner.html rename to docs/reference/lazy_listener/runner.html diff --git a/docs/static/api-docs/slack_bolt/lazy_listener/thread_runner.html b/docs/reference/lazy_listener/thread_runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/lazy_listener/thread_runner.html rename to docs/reference/lazy_listener/thread_runner.html diff --git a/docs/static/api-docs/slack_bolt/listener/async_builtins.html b/docs/reference/listener/async_builtins.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/async_builtins.html rename to docs/reference/listener/async_builtins.html diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener.html b/docs/reference/listener/async_listener.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/async_listener.html rename to docs/reference/listener/async_listener.html diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener_completion_handler.html b/docs/reference/listener/async_listener_completion_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/async_listener_completion_handler.html rename to docs/reference/listener/async_listener_completion_handler.html diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html b/docs/reference/listener/async_listener_error_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/async_listener_error_handler.html rename to docs/reference/listener/async_listener_error_handler.html diff --git a/docs/static/api-docs/slack_bolt/listener/async_listener_start_handler.html b/docs/reference/listener/async_listener_start_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/async_listener_start_handler.html rename to docs/reference/listener/async_listener_start_handler.html diff --git a/docs/static/api-docs/slack_bolt/listener/asyncio_runner.html b/docs/reference/listener/asyncio_runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/asyncio_runner.html rename to docs/reference/listener/asyncio_runner.html diff --git a/docs/static/api-docs/slack_bolt/listener/builtins.html b/docs/reference/listener/builtins.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/builtins.html rename to docs/reference/listener/builtins.html diff --git a/docs/static/api-docs/slack_bolt/listener/custom_listener.html b/docs/reference/listener/custom_listener.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/custom_listener.html rename to docs/reference/listener/custom_listener.html diff --git a/docs/static/api-docs/slack_bolt/listener/index.html b/docs/reference/listener/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/index.html rename to docs/reference/listener/index.html diff --git a/docs/static/api-docs/slack_bolt/listener/listener.html b/docs/reference/listener/listener.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/listener.html rename to docs/reference/listener/listener.html diff --git a/docs/static/api-docs/slack_bolt/listener/listener_completion_handler.html b/docs/reference/listener/listener_completion_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/listener_completion_handler.html rename to docs/reference/listener/listener_completion_handler.html diff --git a/docs/static/api-docs/slack_bolt/listener/listener_error_handler.html b/docs/reference/listener/listener_error_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/listener_error_handler.html rename to docs/reference/listener/listener_error_handler.html diff --git a/docs/static/api-docs/slack_bolt/listener/listener_start_handler.html b/docs/reference/listener/listener_start_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/listener_start_handler.html rename to docs/reference/listener/listener_start_handler.html diff --git a/docs/static/api-docs/slack_bolt/listener/thread_runner.html b/docs/reference/listener/thread_runner.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener/thread_runner.html rename to docs/reference/listener/thread_runner.html diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html b/docs/reference/listener_matcher/async_builtins.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener_matcher/async_builtins.html rename to docs/reference/listener_matcher/async_builtins.html diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html b/docs/reference/listener_matcher/async_listener_matcher.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener_matcher/async_listener_matcher.html rename to docs/reference/listener_matcher/async_listener_matcher.html diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/builtins.html b/docs/reference/listener_matcher/builtins.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener_matcher/builtins.html rename to docs/reference/listener_matcher/builtins.html diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html b/docs/reference/listener_matcher/custom_listener_matcher.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener_matcher/custom_listener_matcher.html rename to docs/reference/listener_matcher/custom_listener_matcher.html diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/index.html b/docs/reference/listener_matcher/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener_matcher/index.html rename to docs/reference/listener_matcher/index.html diff --git a/docs/static/api-docs/slack_bolt/listener_matcher/listener_matcher.html b/docs/reference/listener_matcher/listener_matcher.html similarity index 100% rename from docs/static/api-docs/slack_bolt/listener_matcher/listener_matcher.html rename to docs/reference/listener_matcher/listener_matcher.html diff --git a/docs/static/api-docs/slack_bolt/logger/index.html b/docs/reference/logger/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/logger/index.html rename to docs/reference/logger/index.html diff --git a/docs/static/api-docs/slack_bolt/logger/messages.html b/docs/reference/logger/messages.html similarity index 100% rename from docs/static/api-docs/slack_bolt/logger/messages.html rename to docs/reference/logger/messages.html diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html b/docs/reference/middleware/assistant/assistant.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/assistant/assistant.html rename to docs/reference/middleware/assistant/assistant.html diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html b/docs/reference/middleware/assistant/async_assistant.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/assistant/async_assistant.html rename to docs/reference/middleware/assistant/async_assistant.html diff --git a/docs/static/api-docs/slack_bolt/middleware/assistant/index.html b/docs/reference/middleware/assistant/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/assistant/index.html rename to docs/reference/middleware/assistant/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/async_builtins.html b/docs/reference/middleware/async_builtins.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/async_builtins.html rename to docs/reference/middleware/async_builtins.html diff --git a/docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html b/docs/reference/middleware/async_custom_middleware.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/async_custom_middleware.html rename to docs/reference/middleware/async_custom_middleware.html diff --git a/docs/static/api-docs/slack_bolt/middleware/async_middleware.html b/docs/reference/middleware/async_middleware.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/async_middleware.html rename to docs/reference/middleware/async_middleware.html diff --git a/docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html b/docs/reference/middleware/async_middleware_error_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/async_middleware_error_handler.html rename to docs/reference/middleware/async_middleware_error_handler.html diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html b/docs/reference/middleware/attaching_function_token/async_attaching_function_token.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.html rename to docs/reference/middleware/attaching_function_token/async_attaching_function_token.html diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html b/docs/reference/middleware/attaching_function_token/attaching_function_token.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/attaching_function_token/attaching_function_token.html rename to docs/reference/middleware/attaching_function_token/attaching_function_token.html diff --git a/docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html b/docs/reference/middleware/attaching_function_token/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/attaching_function_token/index.html rename to docs/reference/middleware/attaching_function_token/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_authorization.html b/docs/reference/middleware/authorization/async_authorization.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/async_authorization.html rename to docs/reference/middleware/authorization/async_authorization.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_internals.html b/docs/reference/middleware/authorization/async_internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/async_internals.html rename to docs/reference/middleware/authorization/async_internals.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html b/docs/reference/middleware/authorization/async_multi_teams_authorization.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/async_multi_teams_authorization.html rename to docs/reference/middleware/authorization/async_multi_teams_authorization.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html b/docs/reference/middleware/authorization/async_single_team_authorization.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/async_single_team_authorization.html rename to docs/reference/middleware/authorization/async_single_team_authorization.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/authorization.html b/docs/reference/middleware/authorization/authorization.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/authorization.html rename to docs/reference/middleware/authorization/authorization.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/index.html b/docs/reference/middleware/authorization/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/index.html rename to docs/reference/middleware/authorization/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/internals.html b/docs/reference/middleware/authorization/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/internals.html rename to docs/reference/middleware/authorization/internals.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html b/docs/reference/middleware/authorization/multi_teams_authorization.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/multi_teams_authorization.html rename to docs/reference/middleware/authorization/multi_teams_authorization.html diff --git a/docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html b/docs/reference/middleware/authorization/single_team_authorization.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/authorization/single_team_authorization.html rename to docs/reference/middleware/authorization/single_team_authorization.html diff --git a/docs/static/api-docs/slack_bolt/middleware/custom_middleware.html b/docs/reference/middleware/custom_middleware.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/custom_middleware.html rename to docs/reference/middleware/custom_middleware.html diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html b/docs/reference/middleware/ignoring_self_events/async_ignoring_self_events.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/async_ignoring_self_events.html rename to docs/reference/middleware/ignoring_self_events/async_ignoring_self_events.html diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html b/docs/reference/middleware/ignoring_self_events/ignoring_self_events.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/ignoring_self_events.html rename to docs/reference/middleware/ignoring_self_events/ignoring_self_events.html diff --git a/docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html b/docs/reference/middleware/ignoring_self_events/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/ignoring_self_events/index.html rename to docs/reference/middleware/ignoring_self_events/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/index.html b/docs/reference/middleware/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/index.html rename to docs/reference/middleware/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html b/docs/reference/middleware/message_listener_matches/async_message_listener_matches.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/message_listener_matches/async_message_listener_matches.html rename to docs/reference/middleware/message_listener_matches/async_message_listener_matches.html diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html b/docs/reference/middleware/message_listener_matches/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/message_listener_matches/index.html rename to docs/reference/middleware/message_listener_matches/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html b/docs/reference/middleware/message_listener_matches/message_listener_matches.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/message_listener_matches/message_listener_matches.html rename to docs/reference/middleware/message_listener_matches/message_listener_matches.html diff --git a/docs/static/api-docs/slack_bolt/middleware/middleware.html b/docs/reference/middleware/middleware.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/middleware.html rename to docs/reference/middleware/middleware.html diff --git a/docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html b/docs/reference/middleware/middleware_error_handler.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/middleware_error_handler.html rename to docs/reference/middleware/middleware_error_handler.html diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/async_request_verification.html b/docs/reference/middleware/request_verification/async_request_verification.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/request_verification/async_request_verification.html rename to docs/reference/middleware/request_verification/async_request_verification.html diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/index.html b/docs/reference/middleware/request_verification/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/request_verification/index.html rename to docs/reference/middleware/request_verification/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html b/docs/reference/middleware/request_verification/request_verification.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/request_verification/request_verification.html rename to docs/reference/middleware/request_verification/request_verification.html diff --git a/docs/static/api-docs/slack_bolt/middleware/ssl_check/async_ssl_check.html b/docs/reference/middleware/ssl_check/async_ssl_check.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/ssl_check/async_ssl_check.html rename to docs/reference/middleware/ssl_check/async_ssl_check.html diff --git a/docs/static/api-docs/slack_bolt/middleware/ssl_check/index.html b/docs/reference/middleware/ssl_check/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/ssl_check/index.html rename to docs/reference/middleware/ssl_check/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/ssl_check/ssl_check.html b/docs/reference/middleware/ssl_check/ssl_check.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/ssl_check/ssl_check.html rename to docs/reference/middleware/ssl_check/ssl_check.html diff --git a/docs/static/api-docs/slack_bolt/middleware/url_verification/async_url_verification.html b/docs/reference/middleware/url_verification/async_url_verification.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/url_verification/async_url_verification.html rename to docs/reference/middleware/url_verification/async_url_verification.html diff --git a/docs/static/api-docs/slack_bolt/middleware/url_verification/index.html b/docs/reference/middleware/url_verification/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/url_verification/index.html rename to docs/reference/middleware/url_verification/index.html diff --git a/docs/static/api-docs/slack_bolt/middleware/url_verification/url_verification.html b/docs/reference/middleware/url_verification/url_verification.html similarity index 100% rename from docs/static/api-docs/slack_bolt/middleware/url_verification/url_verification.html rename to docs/reference/middleware/url_verification/url_verification.html diff --git a/docs/static/api-docs/slack_bolt/oauth/async_callback_options.html b/docs/reference/oauth/async_callback_options.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/async_callback_options.html rename to docs/reference/oauth/async_callback_options.html diff --git a/docs/static/api-docs/slack_bolt/oauth/async_internals.html b/docs/reference/oauth/async_internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/async_internals.html rename to docs/reference/oauth/async_internals.html diff --git a/docs/static/api-docs/slack_bolt/oauth/async_oauth_flow.html b/docs/reference/oauth/async_oauth_flow.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/async_oauth_flow.html rename to docs/reference/oauth/async_oauth_flow.html diff --git a/docs/static/api-docs/slack_bolt/oauth/async_oauth_settings.html b/docs/reference/oauth/async_oauth_settings.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/async_oauth_settings.html rename to docs/reference/oauth/async_oauth_settings.html diff --git a/docs/static/api-docs/slack_bolt/oauth/callback_options.html b/docs/reference/oauth/callback_options.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/callback_options.html rename to docs/reference/oauth/callback_options.html diff --git a/docs/static/api-docs/slack_bolt/oauth/index.html b/docs/reference/oauth/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/index.html rename to docs/reference/oauth/index.html diff --git a/docs/static/api-docs/slack_bolt/oauth/internals.html b/docs/reference/oauth/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/internals.html rename to docs/reference/oauth/internals.html diff --git a/docs/static/api-docs/slack_bolt/oauth/oauth_flow.html b/docs/reference/oauth/oauth_flow.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/oauth_flow.html rename to docs/reference/oauth/oauth_flow.html diff --git a/docs/static/api-docs/slack_bolt/oauth/oauth_settings.html b/docs/reference/oauth/oauth_settings.html similarity index 100% rename from docs/static/api-docs/slack_bolt/oauth/oauth_settings.html rename to docs/reference/oauth/oauth_settings.html diff --git a/docs/static/api-docs/slack_bolt/request/async_internals.html b/docs/reference/request/async_internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/request/async_internals.html rename to docs/reference/request/async_internals.html diff --git a/docs/static/api-docs/slack_bolt/request/async_request.html b/docs/reference/request/async_request.html similarity index 100% rename from docs/static/api-docs/slack_bolt/request/async_request.html rename to docs/reference/request/async_request.html diff --git a/docs/static/api-docs/slack_bolt/request/index.html b/docs/reference/request/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/request/index.html rename to docs/reference/request/index.html diff --git a/docs/static/api-docs/slack_bolt/request/internals.html b/docs/reference/request/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/request/internals.html rename to docs/reference/request/internals.html diff --git a/docs/static/api-docs/slack_bolt/request/payload_utils.html b/docs/reference/request/payload_utils.html similarity index 100% rename from docs/static/api-docs/slack_bolt/request/payload_utils.html rename to docs/reference/request/payload_utils.html diff --git a/docs/static/api-docs/slack_bolt/request/request.html b/docs/reference/request/request.html similarity index 100% rename from docs/static/api-docs/slack_bolt/request/request.html rename to docs/reference/request/request.html diff --git a/docs/static/api-docs/slack_bolt/response/index.html b/docs/reference/response/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/response/index.html rename to docs/reference/response/index.html diff --git a/docs/static/api-docs/slack_bolt/response/response.html b/docs/reference/response/response.html similarity index 100% rename from docs/static/api-docs/slack_bolt/response/response.html rename to docs/reference/response/response.html diff --git a/docs/static/api-docs/slack_bolt/util/async_utils.html b/docs/reference/util/async_utils.html similarity index 100% rename from docs/static/api-docs/slack_bolt/util/async_utils.html rename to docs/reference/util/async_utils.html diff --git a/docs/static/api-docs/slack_bolt/util/index.html b/docs/reference/util/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/util/index.html rename to docs/reference/util/index.html diff --git a/docs/static/api-docs/slack_bolt/util/utils.html b/docs/reference/util/utils.html similarity index 100% rename from docs/static/api-docs/slack_bolt/util/utils.html rename to docs/reference/util/utils.html diff --git a/docs/static/api-docs/slack_bolt/version.html b/docs/reference/version.html similarity index 100% rename from docs/static/api-docs/slack_bolt/version.html rename to docs/reference/version.html diff --git a/docs/static/api-docs/slack_bolt/workflows/index.html b/docs/reference/workflows/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/index.html rename to docs/reference/workflows/index.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/async_step.html b/docs/reference/workflows/step/async_step.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/async_step.html rename to docs/reference/workflows/step/async_step.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html b/docs/reference/workflows/step/async_step_middleware.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/async_step_middleware.html rename to docs/reference/workflows/step/async_step_middleware.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/index.html b/docs/reference/workflows/step/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/index.html rename to docs/reference/workflows/step/index.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/internals.html b/docs/reference/workflows/step/internals.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/internals.html rename to docs/reference/workflows/step/internals.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/step.html b/docs/reference/workflows/step/step.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/step.html rename to docs/reference/workflows/step/step.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html b/docs/reference/workflows/step/step_middleware.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/step_middleware.html rename to docs/reference/workflows/step/step_middleware.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_complete.html b/docs/reference/workflows/step/utilities/async_complete.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/async_complete.html rename to docs/reference/workflows/step/utilities/async_complete.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_configure.html b/docs/reference/workflows/step/utilities/async_configure.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/async_configure.html rename to docs/reference/workflows/step/utilities/async_configure.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_fail.html b/docs/reference/workflows/step/utilities/async_fail.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/async_fail.html rename to docs/reference/workflows/step/utilities/async_fail.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/async_update.html b/docs/reference/workflows/step/utilities/async_update.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/async_update.html rename to docs/reference/workflows/step/utilities/async_update.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/complete.html b/docs/reference/workflows/step/utilities/complete.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/complete.html rename to docs/reference/workflows/step/utilities/complete.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/configure.html b/docs/reference/workflows/step/utilities/configure.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/configure.html rename to docs/reference/workflows/step/utilities/configure.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/fail.html b/docs/reference/workflows/step/utilities/fail.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/fail.html rename to docs/reference/workflows/step/utilities/fail.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/index.html b/docs/reference/workflows/step/utilities/index.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/index.html rename to docs/reference/workflows/step/utilities/index.html diff --git a/docs/static/api-docs/slack_bolt/workflows/step/utilities/update.html b/docs/reference/workflows/step/utilities/update.html similarity index 100% rename from docs/static/api-docs/slack_bolt/workflows/step/utilities/update.html rename to docs/reference/workflows/step/utilities/update.html diff --git a/docs/sidebars.js b/docs/sidebars.js deleted file mode 100644 index decb8cccb..000000000 --- a/docs/sidebars.js +++ /dev/null @@ -1,127 +0,0 @@ -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebars = { - sidebarBoltPy: [ - { - type: 'doc', - id: 'index', - label: 'Bolt for Python', - className: 'sidebar-title', - }, - { - type: 'doc', - id: 'getting-started', - }, - { type: 'html', value: '


    ' }, - { - type: 'category', - label: 'Guides', - collapsed: false, - items: [ - "building-an-app", - { - type: "category", - label: "Slack API calls", - items: ["concepts/message-sending", "concepts/web-api"], - }, - { - type: "category", - label: "Events", - items: ["concepts/message-listening", "concepts/event-listening"], - }, - { - type: "category", - label: "App UI & Interactivity", - items: [ - "concepts/acknowledge", - "concepts/shortcuts", - "concepts/commands", - "concepts/actions", - "concepts/opening-modals", - "concepts/updating-pushing-views", - "concepts/view-submissions", - "concepts/select-menu-options", - "concepts/app-home", - ], - }, - "concepts/ai-apps", - { - type: 'category', - label: 'Custom Steps', - items: [ - 'concepts/custom-steps', - 'concepts/custom-steps-dynamic-options', - ] - }, - { - type: "category", - label: "App Configuration", - items: [ - "concepts/socket-mode", - "concepts/errors", - "concepts/logging", - "concepts/async", - ], - }, - { - type: "category", - label: "Middleware & Context", - items: [ - "concepts/global-middleware", - "concepts/listener-middleware", - "concepts/context", - ], - }, - "concepts/lazy-listeners", - { - type: "category", - label: "Adaptors", - items: ["concepts/adapters", "concepts/custom-adapters"], - }, - { - type: "category", - label: "Authorization & Security", - items: [ - "concepts/authenticating-oauth", - "concepts/authorization", - "concepts/token-rotation", - ], - }, - { - type: "category", - label: "Legacy", - items: ["concepts/steps-from-apps"], - }, - ], - }, - { type: "html", value: "
    " }, - { - type: "category", - label: "Tutorials", - items: ["tutorial/ai-chatbot", "tutorial/custom-steps", "tutorial/custom-steps-for-jira", "tutorial/custom-steps-workflow-builder-new", "tutorial/custom-steps-workflow-builder-existing", "tutorial/modals"], - }, - { type: "html", value: "
    " }, - { - type: "link", - label: "Reference", - href: "https://tools.slack.dev/bolt-python/api-docs/slack_bolt/", - }, - { type: "html", value: "
    " }, - { - type: "link", - label: "Release notes", - href: "https://github.com/slackapi/bolt-python/releases", - }, - { - type: "link", - label: "Code on GitHub", - href: "https://github.com/SlackAPI/bolt-python", - }, - { - type: "link", - label: "Contributors Guide", - href: "https://github.com/SlackAPI/bolt-python/blob/main/.github/contributing.md", - }, - ], -}; - -export default sidebars; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css deleted file mode 100644 index 8a0fa6ca2..000000000 --- a/docs/src/css/custom.css +++ /dev/null @@ -1,583 +0,0 @@ -:root { - --ifm-font-size-base: 15px; - - /* set hex colors here pls */ - --dim: #eef2f6; - - --aubergine: #481a54; - --aubergine-background: #552555; - --aubergine-dark: #2c0134; - - --aubergine-active: #7c3085; - --aubergine-active-70: #7c308570; - --aubergine-active-50: #7c308550; - --aubergine-active-30: #7c308530; - - --horchata: #f4ede4; - - --slack-red: #e3066a; - --slack-red-70: #e3066a70; - --slack-red-50: #e3066a50; - --slack-red-30: #e3066a30; - --slack-red-20: #e3066a20; - - --slack-yellow: #fcc003; - --slack-yellow-70: #fcc00370; - --slack-yellow-50: #fcc00350; - --slack-yellow-30: #fcc00330; - --slack-yellow-20: #fcc00320; - - --slack-green: #41b658; - --slack-green-70: #41b65870; - --slack-green-50: #41b65850; - --slack-green-30: #41b65830; - --slack-green-20: #41b65820; - - --slack-blue: #1ab9ff; - --slack-blue-70: #1ab9ff70; - --slack-blue-50: #1ab9ff70; - --slack-blue-30: #1ab9ff30; - --slack-blue-20: #1ab9ff20; - - /* used for dark-mode links */ - --slack-cloud-blue: #1ab9ff; - /* slack marketing color used for light-mode links */ - --slack-dark-blue: #1264a3; - - /* used for functions */ - --unofficial-orange: #e36606; - --unofficial-orange-70: #e3660670; - --unofficial-orange-50: #e3660650; - --unofficial-orange-30: #e3660630; - - /* turns opacity into flat colors for bubbles on top of things */ - --slack-yellow-70-flat: #fcc00370; - - --slack-yellow-30-on-white: #feecb3; - --slack-green-30-on-white: #c6e9cc; - --slack-red-30-on-white: #f6b4d2; - --slack-blue-30-on-white: #baeaff; - --unofficial-orange-30-on-white: #f6d1b4; - --aubergine-active-30-on-white: #d7c0da; - - --ifm-h5-font-size: 1rem; - /* --ifm-heading-font-family: 'AvantGardeForSalesforce', sans-serif; */ - /* --ifm-font-family-base: 'Salesforce_Sans', sans-serif; */ - --ifm-navbar-height: 83px; - - -} - -.navbar__logo img { - height: 150%; - margin-top: -8px; -} - -.navbar--dark { - --ifm-navbar-background-color: #000 !important; - --ifm-navbar-link-hover-color: var(--slack-blue); -} - -.footer { - --ifm-footer-background-color: #000 !important; - --ifm-footer-link-hover-color: var(--slack-blue); - --ifm-footer-color: white !important; -} - -.theme-admonition div{ - text-transform: none !important; /* Disables uppercase transformation */ - -} - -/* resets striped tables that hurt me eyes */ -table tr:nth-child(even) { - background-color: inherit; -} - -h1 { - font-size: 2.5rem; -} - -/* Reduce title size in blog list */ -.blog-list-page h2[class*="title"] -{ - font-size: 2rem; -} - -/* Reduce title size in blog page */ -.blog-post-page h1[class*="title"] -{ - font-size: 2rem; -} - -/* changing the links to blue for accessibility */ -p a, -.markdown a { - color: var(--slack-cloud-blue); - text-decoration: none; -} - -p a, -.markdown a:hover { - text-decoration: underline; -} - -a:hover { - color: var(--slack-cloud-blue); -} - -.article h1 { - font-size: 1rem !important; /* Adjust the size as needed */ -} - -.card { - box-shadow: none; -} - -/* adjusting for light and dark modes */ -[data-theme="light"] { - --docusaurus-highlighted-code-line-bg: var(--dim); - --ifm-color-primary: var(--aubergine-active); - --ifm-navbar-background-color: black; - --ifm-footer-background-color: black; - --slack-cloud-blue: var(--slack-dark-blue); - --reference-section-color: var(--horchata); -} - -[data-theme="dark"] { - --docusaurus-highlighted-code-line-bg: rgb(0 0 0 / 30%); - --ifm-color-primary: var(--slack-cloud-blue); - --ifm-navbar-background-color: #000 !important; - --ifm-footer-background-color: #000 !important; - --ifm-footer-color: white; -} - -.alert--warning { - --ifm-alert-background-color: var(--slack-yellow-30); - --ifm-alert-border-color: var(--slack-yellow); - --ifm-alert-background-color-highlight: var(--slack-yellow-30); -} - -.alert--info { - --ifm-alert-background-color: var(--slack-blue-30); - --ifm-alert-border-color: var(--slack-blue); - /* --ifm-alert-background-color-highlight: var(--slack-blue-30); */ -} - -.alert--danger { - --ifm-alert-background-color: var(--slack-red-30); - --ifm-alert-border-color: var(--slack-red); -} - -.alert--success { - --ifm-alert-background-color: var(--slack-green-30); - --ifm-alert-border-color: var(--slack-green); -} - -.footer { - /* font-size: 80%; */ - padding-bottom: 0.5rem; -} - -.footer__items a { - color: inherit; -} - -.footer .container { - margin: 0; -} - -.table-of-contents__link { - font-size: .9rem; -} - -/* bolding ToC for contrast */ -.table-of-contents__link--active { - font-weight: bold; -} - -/* removing ToC line */ -.table-of-contents__left-border { - border-left: none !important; -} - - -.dropdown-hr { - margin: 0 -} - -/* increasing name of site in sidebar */ -.sidebar-title { - /* padding-bottom: 0.5rem; - font-size: 1.25em; */ - font-weight: bold; -} - -.theme-doc-sidebar-item-link hr { - margin: 1rem; -} - -.sidebar-sdk-title { - /* margin: 0.5rem 0; */ - padding: 0.5rem; - /* border-radius: 4px; */ - border-bottom: 0.5px solid grey; -} - -/* .theme-doc-sidebar-item-category-level-1 .menu__link { - font-weight: bold; -} */ - -.theme-doc-sidebar-item-category-level-1 .menu__list-item .menu__link { - font-weight: normal; -} - -/* removing sidebar line and adding space to match ToC */ -.theme-doc-sidebar-container { - border-right: none !important; - margin-right: 2rem; -} - -/* announcement bar up top */ -div[class^="announcementBar_"] { - font-size: 20px; - height: 50px; - background: var(--horchata); -} - -/* navbar github link */ -.navbar-github-link { - width: 32px; - height: 32px; - padding: 6px; - margin-right: 6px; - margin-left: 6px; - border-radius: 50%; - transition: background var(--ifm-transition-fast); -} - -.navbar-github-link:hover { - background: var(--ifm-color-gray-800); -} - -.navbar-github-link::before { - content: ""; - height: 100%; - display: block; - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") - no-repeat; -} - -/* Delineate tab blocks */ -.tabs-container { - border: 1px solid var(--ifm-color-primary); - border-radius: 4px; - padding: 0.5em; -} - -summary { - background-color: var(--ifm-background-color); - --docusaurus-details-decoration-color: var(--ifm-color-primary); -} - -details { - border: 1px solid var(--ifm-color-primary)!important; - background-color: var(--ifm-background-color)!important; - --docusaurus-details-decoration-color: var(--ifm-color-primary); -} - -details[open] { - border: 1px solid var(--ifm-color-primary); - background-color: var(--ifm-background-color); - --docusaurus-details-decoration-color: var(--ifm-color-primary); - -} - -/* Docs code bubbles */ -[data-theme="light"] { - --contrast-color: black; - --code-link-background: var(--slack-blue-30); - --code-link-text: rgb(21, 50, 59); - - --method-link-background: var(--slack-green-30-on-white); - --method-link-text: rgb(0, 41, 0); - - --scope-link-background: var(--slack-yellow-30-on-white); - --scope-link-text: rgb(63, 46, 0); - - --event-link-background:#fad4e5; - /* --event-link-text: rgb(63, 0, 24); */ - --event-link-text: rgb(0, 0, 0); - - --function-link-background: var(--unofficial-orange-30-on-white); - --function-link-text: rgb(75, 35, 0); - - --command-link-background: var(--aubergine-active-30-on-white); - --command-link-text: rgb(75, 0, 75); -} - -[data-theme="dark"] { - --contrast-color: white; - --code-link-text: white; - --method-link-text: white; - --scope-link-text: white; - --event-link-text: white; - --function-link-text: white; - --command-link-text: white; - - --code-link-background: var(--slack-blue-70); - --method-link-background: var(--slack-green-70); - --scope-link-background: var(--slack-yellow-70); - --event-link-background: var(--slack-red-70); - --command-link-background: var(--aubergine-active); - --function-link-background: var(--unofficial-orange-70); -} - -a code { - background-color: var(--code-link-background); - color: var(--code-link-text); -} - -a[href^="https://docs.slack.dev/reference/methods"] > code -{ - background-color: var(--method-link-background); - color: var(--method-link-text); -} - -a[href^="/reference/methods"] > code -{ - background-color: var(--method-link-background); - color: var(--method-link-text); -} - -a[href^="https://docs.slack.dev/reference/scopes"] > code -{ - background-color: var(--scope-link-background); - color: var(--scope-link-text); -} - -a[href^="/reference/scopes"] > code -{ - background-color: var(--scope-link-background); - color: var(--scope-link-text); -} - -a[href^="https://docs.slack.dev/reference/events"] > code -{ - background-color: var(--event-link-background); - color: var(--event-link-text); -} - -a[href^="/reference/events"] > code -{ - background-color: var(--event-link-background); - color: var(--event-link-text); -} - -a[href^="/deno-slack-sdk/reference/slack-functions/"] > code { - background-color: var(--function-link-background); - color: var(--function-link-text); -} - -a[href^="/deno-slack-sdk/reference/connector-functions/"] > code { - background-color: var(--function-link-background); - color: var(--function-link-text); -} - -a[href^="/slack-cli/reference/commands"] > code { - background-color: var(--command-link-background); - color: var(--command-link-text); -} - -.facts-section { - margin-top: 2rem; - background-color: var(--slack-green-20) !important; -} - - -.facts-section .tabs-container { - border: none; - border-radius: 0px; - padding: 0em; - --ifm-leading: 0rem - -} - -.facts-section .tabs__item { - padding: 0 0.5rem; - color: inherit; -} - -.facts-section .tabs__item--active { - border-bottom-color: inherit -} - -.errors-section { - background-color: var(--slack-red-20) !important; -} - - -.inputs-section { - background-color: var(--slack-blue-20) !important; -} - -.functions-section { - border-radius: 6px; - padding: 1rem; - margin-bottom: 2rem; -} - -.facts-row-list { - display: flex; - flex-wrap: wrap; - column-gap: 0.5rem; - row-gap: 0.5rem; - align-items: baseline; /* Aligns items to the same baseline */ -} - -.facts-row-list-item { - display: inline-block; -} - - -.inline-icon { - height: 1.9em; /* Matches the height of the text */ - width: auto; /* Maintains aspect ratio */ - vertical-align: middle; /* Aligns with the text */ -} - -.functions-section .type { - text-align: right; -} - -.param-required-section { - padding-top: 1rem; - margin-bottom: 1rem; -} - -.reference-container { - display: flex; - flex-direction: column; - width: 100%; - /* border: 1px solid #ddd; */ - border-radius: 8px; - overflow: hidden; -} -.reference-facts-header { - display: flex; - /* background: #f4f4f4; */ - padding: 10px 0; - font-weight: bold; -} -.reference-facts-item { - display: flex; - padding: 10px 0; - border-bottom: 1px solid var(--ifm-color-emphasis-200); -} -.reference-facts-item:last-child { - border-bottom: none; -} - -.reference-name { - flex: 2; - /* padding: 5px;*/ - min-width: 200px; -} - -.reference-description { - flex: 2; /* Makes description take extra space */ - padding: 5px; -} - -.reference-last-column { - flex: 1; - padding: 5px 0; -} - -.reference-subitems-bubble { - display: inline-block; - background: var(--ifm-color-emphasis-200); - color: var(--ifm-color-emphasis-1000); - padding: 2px 6px; - margin: 2px; - border-radius: 4px; - font-size: 12px; -} - -.param-container { - border-top: 1px solid lightgray; - padding-top: 1rem; - padding-bottom: 1rem; -} - -.param-container:last-child { - padding-bottom: 0; -} - -.param-top-row { - display: flex; - align-items: center; - margin-bottom: 1rem; -} - -/* left-align param name */ -.param-top-row .name { - flex: 1; -} - -/* right-align Required and Type */ -.param-top-row .required, -.param-top-row .type { - margin-left: auto; - text-align: right; -} - -/* add space between Required and Type */ -.param-top-row .required { - margin-left: 10px; -} - -.info-row { - display: flex; - /* align-items: center; */ - padding-top: 1rem; - padding-bottom: 1rem; - border-top: 1px solid var(--ifm-color-emphasis-400); -} - -.info-key { - flex: 0 0 10rem; - align-items: center; -} - -/* hides next and previous */ -.pagination-nav__link { - display: none; -} - -/* -html[data-theme="dark"] .button:hover { - background-color: var(--slack-blue-30-on-white); -} - -html[data-theme="light"] .button:hover { - background-color: var(--aubergine-active-30-on-white); -} */ - -.button { - background-color: var(--aubergine); /* Change color on hover */ - border: 0; - color: white; -} - -.button:hover { - background-color: var(--aubergine-active); - border: 0; - color: white; -} - -.footer-spaced { - display: flex; - gap: 20px; - padding-bottom: 1rem -} \ No newline at end of file diff --git a/docs/src/theme/NotFound/Content/index.js b/docs/src/theme/NotFound/Content/index.js deleted file mode 100644 index c122bc039..000000000 --- a/docs/src/theme/NotFound/Content/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import clsx from 'clsx'; -import Translate from '@docusaurus/Translate'; -import Heading from '@theme/Heading'; -export default function NotFoundContent({className}) { - return ( -
    -
    -
    - - - Oh no! There's nothing here. - - -

    - - If we've led you astray, please let us know. We'll do our best to get things in order. - - -

    -

    - - For now, we suggest heading back to the beginning to get your bearings. May your next journey have clear skies to guide you true. - -

    -
    -
    -
    - ); -} diff --git a/docs/src/theme/NotFound/index.js b/docs/src/theme/NotFound/index.js deleted file mode 100644 index 3b551f9e4..000000000 --- a/docs/src/theme/NotFound/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import {translate} from '@docusaurus/Translate'; -import {PageMetadata} from '@docusaurus/theme-common'; -import Layout from '@theme/Layout'; -import NotFoundContent from '@theme/NotFound/Content'; -export default function Index() { - const title = translate({ - id: 'theme.NotFound.title', - message: 'Page Not Found', - }); - return ( - <> - - - - - - ); -} diff --git a/docs/static/.nojekyll b/docs/static/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/static/img/bolt-logo.svg b/docs/static/img/bolt-logo.svg deleted file mode 100644 index 5077600d5..000000000 --- a/docs/static/img/bolt-logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/static/img/bolt-py-logo.svg b/docs/static/img/bolt-py-logo.svg deleted file mode 100644 index 1dcab5261..000000000 --- a/docs/static/img/bolt-py-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/static/img/boltpy/bolt-favicon.png b/docs/static/img/boltpy/bolt-favicon.png deleted file mode 100644 index bfe5456c172c5e76fa9b13b1e8ab0a793b6e95cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3376 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 diff --git a/docs/static/img/boltpy/ngrok.gif b/docs/static/img/boltpy/ngrok.gif deleted file mode 100644 index c7c94d51a303aedcbee5302988b5b6629940e8b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49094 zcmd?RcU+V0wk`Um2LkxgJ0$cf0RfSsp?3@&Y0{gB0qLS9bdVB?fT$RxS7{;wYUl`v zK~X_aK~O0cKvYnagMQ!IYwdg1*>|7&JNN!`fxi9AzT9cc(SMgBb4PblgVkYDHap`z>oNF$f?s*xQCXWSyRwb)~JRrFTeP zTp&F590b7Z?(Wt%wdoy{g#a9BnOU>5v#~Cwi;IigT%faytDm1Agaxp(vsXQ+H841+ zqopA%Bt$%FsjH_KclMm1xZ-tMiKG60M}38y+}w$YiPO&dXVObg+ZiP1ma51~bai#z z>1;hjJkFtDsA3jWS67=yO}u#V;?t*3$HvAYPlhBUBv|-HOA2!Fu|U$Uw|4an0Du%0 z0GI(lTZ*@?D5Ji+|4vJ}wiw*)bpFxfiLPOZttH8Ax4R1?+^=0stVlmqc9T9hG&IoH z?G_w0&{@^nR$}5Dex6#-0YJD!<=V?Llge+Bvg^9)3y+_@mYiIi7!~As?wT4uvbmzL z_^eM=b#?2VzAN<|qTKsVQ?jn5pTTencC^&g=bf`q6U?vcs>wRv+E9Lqa*c^s{7UUj zsDNf?Tm4~q32q^Y_$$r5H%qR?d)sJG}Z;!tL-4er(`$6$o57V?AzswBbB?7@@L zOF3|Dgm`SB<4B$9^oL{1%ery?2a4*fj-ym``wnQFH8jrh58aYKR1grld(88-fA9-u z|IeOhf{rlwlbIu0(PO|Mnxe|Q>cAs_V~|V`rSjs-X>ViIn@ATNOfH(CI9I7 zedO^l`B0MMLuX`fPajJ*fN6 zuRY{iX^`A3XbBlQ&=09Bf!=O~wA_UO_aLxy@bhP2*)hoCM(FXc@8JFj2o(ZPgaN$( z&gy5>Rrcdy zkiCAuZNm{D00k6&H^=Wb0nobu1TAFJMC(aFbIQ36HQ zlSUql-n#MX9{I>eR*R0N`MzMK#Y^Bb`PV~KadV^M6#H7+N)1 z8;|Cf)u~qLn~it6zI{@cQ?SW0uTv!o0$!)d zR93%ES7={)eE~lxvyh?sG+-f9<8AdqmiE`Bg=_*+c9Ckp6S$aTB2}}PYoWfpm`5~{ zeUoqJ5cuYzlV8o70@ujpH-#R_vTuug3Ig9=3aG4kTO8cJ{Pr?wQ1;!G@TYizl4wcm76(6UJ|-F~ewnm$Jo)8`lYhgPr>;>SzC80tQT#gPQ+V?0bU;+l%OT4c}hIeOvhO?G+ide`_w0H)Lx*Rl0HOb;iMuTMJa<{o9NA zjv?D`iu@b5-(HFOxc!cnvj6*1d11)+d#NUuUB}{ronUqV#KP zvGCNd?WL+4zrMe}_377-wIQY5oy}*bc7JZYyRrLg=i8^f3S^-HC=)tVV2zBnph0Y@xX2&He`5%1`01Bre zLVL=^2>|F6K$;2=_WB;w^x<>W@ItXAVVIIlsA7ozB%Dp6MvfPDFiRpXd`APnI2_Nf zlS?9T5JuwVkKRqZA2v6}gtLf7Gn6Y>IB~KPv?-i-N~wfJ8%vM35wozcLo9)R4xYlM zsX;bTreK*sH{Wu+GBhPERVbEtWdbKgW@m~^B$+qW^^pJOBnY5T1y}=9z{|b=Z5Oc2 zF9K-)mXnY=V`~KPADm=!-AG7Sv*0gI;?>z&ql!t6UDi0g!m_LjWdhagKMI+(Esd85 zX@sMLeshvo4wN{;^_!D;&8~KGB{J$g`gmT9*rU+R0gvC#YElf)q8i<`SIwg&Y&ax* zG)$;u<8&|rhH0GQG!WP)5x2z0u7^zNp^6{aq+%_hC>2c&K;d@lyLgdk6a0KxLdK(O zw#NG@(RxQYR7|T&+ztlzu(QRL_bA7=e|pa0ry;Yxb!)v%!ic&R+kN}5UU z%}FO-jsNDPxxNd3IBEOy(&PVsa?*9E0QgU-;s#(qCE)R=EMRCMK7jl`OO;+cO!052 z0+pHJf22y(kjLP+R29e_AGu=M>NQrpR~FqhtyM=0w9hjOk0{Gbl$nSMSdO&Gk}Isr z*plRKu2|(bwEiluy(w=|@7_1EDA!(Z+~W7x*`V&XRFM`&uKp!epI2V|B~>tt$RDXn z<&<|H>u6s1Emb8Jw_Dzf79BY8a%`_GeoK{zWoO&+<7(SF_XnLfSD$5=Ch{rk4Wg!m zOs^D$HW)IaWJ#{0)a&q%Y2to9Ko0~+nZC(7!tnfH@Ogp7bgoGEwLVJdY5n?cQBT=` znh2X7u+-E8i%gPK&)AylbB|OK)Wz$0}L1fX0_O(CKOFiNNLDvf}DMtZ(@z&qB0U-P4jrYI2DI* zy;Q~OXV1})I8v#uXc5?ZQZ$^`8mdEW;yA%XozrJVCpGo3>?V=osrz!k1s3rpBR!<3 zQyw-K7mZIq9j<4}VUr#vzX{R_2YOjBXXkptxRR(4fUD8Lm-(Zj0W^x+*Ter(t#t?G z3Odku`HDzBBOyqL7GtSj;IK$xi>#G`v8l8Ha%_c%?ew5R61%kLXiadMh_-hySVY;D z@aQq)5<5VYW*ehcUuujFxmKeXD^-2l1`e&$sQSPJFpfo!v@0$~t1 z3eI8z(#SGTZg@yK`+^X%J*#_PEo&!1ArXQSbzJ{mR!FJ}@C_`F9KNxy=&GuJ#Wz z9j8}cKIppjY0qt9M6G_i&EY)xJ-7M4Vs^FPZu8$T`>No_RsTz#f81uY9TdvL$4){1 zahuEr9)y1)DYS=V8gTRvl6$^g2LBsLrhoPAIbwQ=+<*D@!3!vzKfYa`%4E6c+rz|0 z07dVS{OzzkBn7rjO!DHB(u*|`n0-_A9`J+0lU#siTMgr|dj~qMiL2sq-NDar4LnuO zw%)^09ZjR1u%~)Ht`+Lio67|Jm1?hQZLq>Mg+ds_?-v_iWt@U7O)orIdDX|ioq^Yb z_fqbriXUk6B*Vyq(*7WW5Oi9f)y2`fc7Zc>cmAXaB2vtUsY5+Kx)| zeM!1v?pGX@|9dL)-z`x#0yzRQe#7NdqwtwX2tO41cd@|$1>rqRQvb#T^?OLfk`vJT zc!c<9rG3eme-4R87DdF@eJKKehQvi63$1f5G2r)*h_jaC`dmI|R)F6d5@k8*K(YF% zJwrC>^YEG)(@PfOo*}z{I&W7rk4!yEgS~9SwQklp7P;Y}P5N`>G(|WWD@^jY62`jS z8~GCCO{XMaa%sA;{+Yb$u-D_l7H!Gg2hcr<+x&w!07^WQd2-Ew8GE8{%Z|1I?b{c+d6(D~J-?XHsD1wM9h^i?G{O^|MFi0iN6OvKFJc)b+F(92=(yOh*THG*8R7Vef$VNgxSK3nDjz7m& zuaI9jGT&4g`%e-v_>Wig{5P-2OVFT!*LBmlc%A#5LKpgxXbwKWLz!&wn^^Z)pxgk- z4wxv+arBZY`vPLe&{%)VlEYE2Cz*1}=rmB6r>#0)!nQ*4vQbjdzrO2%y?2fH7ZGTp z>Ba*bau(4<0yzZ~RsYAzf!V2}$+JK8Pc?;Mjgv%W#;+W z?#-Krf;VajJ;y5j8P$p$znl)YSv(Lob(flC>g9vnXhC+*y%Q)gyK7NCdc2T_eROMOc z%~p3R^JkXkco;LKt5|vg{(+jn1WLtU8LK<<%`mU;NbG~yj%bsYu$%?^kKLV{vrpKM z-{SvAneL&Xw1-9{VE0FP{}l}lkldHT#{L@(u|H_cMuTDoY2315zgLk)+52>p>%D%f z3?Su7@iH`n0}!7_ZTub;1BC>P-yscE89>^BEfKtKKmf|kE-|m&SE+si7$!MOs`!xm zL?NmqVeFbQH>=$Z=LcrghWn*zPz4CX7tN)lR;$27{1)PTlMFlPNrcl2WW8Eg_H)dU zUf-j@64NrkOd~0$j#(_=gZF!I1*KnU@u-3DSPGSA@)`>GJ*kIzi2US;2)yoa(~19c zKN`Rv3;(Rn0cf0jG<@he+T#tHj4{1FRsBdC*pwME*0gNop%>zH0dl7f$J`kz;Fs<5 zZrmN`CZ{R~UmM{)mn7%&?A9w$nnB+?seHh|;3)!~Ncg^9g*Af<wtGoQR$n(t{S^e?>de|QyXWvct1SCLiDig5TJaUuMkIDWHEZre>TgZG=nrA#mK$m^cl=%*LV2CIsj)Qp;=543S8uMTbZq4Cv(c9^w8E~ zfIoMMNuHF1iVx7gcX)jEW;cT}0dpXnA388PS?dv_{*R-IV;wD!B6=8<|^7|JZmDDO1xBKTg`oKdiS-@!m}F! z?HRxFHGqTHK zbcq5YU9of$>Zx>RjzbUt%}h#@pf7MEVguwysRWtYVXr(+hplNleU--;=Hr&bEcw`U zlj~`2@f^%R%WN19P@feeUOvU_laNf?6f-@ehCc1*u%;LzofPu4YHy{k7mk8cz;Im- zTcL6fn5MgRPi8k$0LYwd>0G1F3|-Sl9O%$JrWa>I9aK=L-X)SvIN*!!lbuWcoLWB)wJ`HYt;Oyjy z)O}MTPh=${SlwA!GL+4kOci+N$gn50#*(+{@HWQWv@RVY)lZ<;A|px%Cxol534T{a zNV+Iti5IF*=Gfszd_B+gSOJX8i4VS62BY;+F$Z_4Ed+zL%OCKwE4d6?m&aH8MVFG< zXUYz9+Ogm)N>QH(F*7btW+RNd>`I{9C%xvzf+Y{Wh@A1-B2=DkeE-t&4J&ZVjlJ|W zA`8!%?*u$uz;*bvG_|pT4cfQW%c^EwGRxBZk&S4}DPlxN18n<8#_s)KM^LW|;-CW+CHediy zgx&}qCyw#Zu(oL?sV0nxnq!}f3j;Usx3v`F$zBwL{WUO%2P*Oi;))?8E-AxOh7RfT;91vZy|mnJb`MB^)(?t{X7t%QuN{}19W^?`vFqW z=eU!73salb_b6-G*z-G=-g(_T-MDqf1nppZ_GaP>6ZHOKvTb}CS(wTH#A zJrpgpgnX&Clwv~#FnMI{b^u#1FU-Qy#v?Jx!5aGs?>}s=O-Y+Bq$A%3;-UuZIBRnJ*k>rU!k*#kD3Ny5ludp$ROI(XMmt$Bf z4>Eb5W9C6L$1X0tI+;}_404@MB2(5dRkfCq5wy6Ck^;TX(-b_;l{a zk5|CH4m!$eEnaYE4t=zvmv?+ES!-t=%zjAy1AQkB% zb{-r&(7$nXXlFt3C5y9AKeC^ctn%_4<+wy2e-~9d&muea{&50-(?Qiht818sdnKH# zQ-&%#CDd_8k@f12{rgi(Ga8$~Eo&R2xl49CPp%#QvhO@hB{zrV4W^&B?bg}#)A!E2 zU2y-_U1htwWPAi%wte+|sF3UZMcj{>(?@oq2!m_W_qNYH^WblbKC@K7&MG+E%kTV+ zWt@gqr=cH8kl_Jj3meua0PB2|7;Nc0&;g7xn8xY;qaJan(DU2RR7ck+*eP!HCYBKz z`YtW%8!BFOm??%z=`M{@;G!^3k)jwe=?viRoCEvT`9}l_Pb%fgTmo+@%l;`~N1Ed7 zn9xQ_I8+obH_XIKC$nwwacd;*pF%L264mfYUxrSpfsqpg7l6SM;2(JqhiStm!zjr? zCg20`oVbY-24I~aP{uv1Pj8VkQORvzAW@{~C@@tN5AoTGPoGO6%cLp>#BoI@2k!EP zUIq4+o+;hBDUqouY|&}q2a=Mix$%5yCsR|Kz|<&O@+p&~sE)LAg)Ak~VAWL8j+D~Y zH;7_2%Wc&pR)BRJWF18bbPiuYtfz;zu{cYE0Vb4r=?paPLUz%)cMI|1u!QkpamG~Y z-Hr=)hDrH_cKsd6ZR8Btdh*;-`V-=XTT4+hPGGTdmR(WC8<|Y+>XfF`5TmWE9y*yN zATDYRetllCk$@Ez-SKsta<;;#ehVCBI@Vp5KfOY*HlotIzVWbDRS$A30&h-)WahzRp!t1LeX6h_f7b1_JX}p)OaOQ^5L`v)b5G|oqT@IC z@=Da5yaEyr;>eVP5LUeZy{0(vDejXs(hR=rieU*K6BlW|M6uf$DIMn%BhFL4A)^@a zJtWA~P!f6CJ&FVg*MLl+5<*R-PuGx)_>%cJFubI~(Oin%G1ja634yl@Xg%lKlAxo7 zMe#Mc5!07W7h(3y!8kF`YdQFI2TSMB`6Ex6yrd)DbXmOKvGB=)cb8e{mvS1nD7DWJ z_h}dKTbEGQmv7a?T8Lq~VhThx(r2O}lT25h-ezecL-86{26neeF6j7kRaeGhIO}g0 z^3pETmstiKOGJmyJU@1IKK05(b>8LWtIa@O>vSL|k(^FDZ|wz}mSI)*fDOoi=vo?U z4h_T14yi@MzRR-7fo?QPb!2Hnc_|BXDLXGs=sAI1fW09n6Tn`V9EpPP=xl$1Ss)${yNnMi>O~ay z%JR}=4-ipy#qwQxU{$W1uBSW?15j51%%ViewP2H`n*&k!d7gKUGbymr?Jz>1k${xK zBjFxq2HvVB;(SNwfRim$hb|FNrVjH|b1G9KfLJzgKL>c!@EM>_gRu5Ayj<})>w%Ca zi{4Hro!8~n!^_zrH12U2HxiHRd{*CZ0FMv2V45@_y}&FxcHTK$hK96bJ^>vCo!&Bs zFznvRk@7ZBF=Y3n%}mnx<`OaE4-!T+WjLGxd*XK#=G6>aSBJc={hDWW_?;T83zS*q zZ3NK=@$gVu69z-_B_o7)0i&bV=zs+EDnB2@I?@r#%e2}IE3N$4W^7;{PAh0(Zf;`< zh1ks@oxa>SAWtILLXPI$khpATl(ctKg75ejUVk%t6PFgl!u@j&V89RIAKumW7*aaB zZ@?h%L>VGQ;61Db>R{b2rW>yeNK93@5v|)dCIdx1pg2Z~1S5Hq9qR)^se;ftFH|hU zDogNou3*E(QNbX5+uZkdI12fr4ciU)s*-QHb=fs~AoLy3!kd6sa;dF)$abIcRURk= z50hGh;7Cv$T?V43HVV1<JuYJ?x5vG$nS1-H z?(MAJgVvJ(Z91}^grfqGIQpp{<(MIe0t1Svw`IHHLco=p^w2RXNH#_fvMFdErGm{E z%Ita|W)46?0Y^f}r)IuPiCR%vLkOS9uqFAu?Da7N8)ym=kZ>ZpH>l@JtW=Rk*#_ zyZ#L5fVtWLOU_Yd%7E<~$1V8aFUk;is3=vAo#izawPX3_IzKC^=8`7Zup!sHt5=(h z1FLG~EW+gLivgDlAu@Pak%MRmi`{uzZQlFZj4^qn^>C495I`Ni%UsW|(|OhFqtsL3ODMzOB?=TX9uu zPnbR#{;Avki;(K_PTgv-+5OJ?2gRV#sQ9L;5X>zI@j;#BJqF@2(pYpXKIlU&_b0O> zS{wG?Vs*E_qkcTU0TOOL9E3>D0ZblHj;5}YI}fm|ke%WqyWj19?LI!-96Iq7t{`x- z{jnskPm`e93mr~8Dg+cf!!`L4259_j+~HQF2nCO_H*=nWLN~bD zM<0iBS&LyHF6Kj5Bk!{@c!d}camFL-okzYp*pJCrXUpc#n@_BGf+L#T(Ka={=Fb|j z&5>7|1ziSEx|YNSev{FLq&t){ExL~nN<*GsYX(v$a8yVKi*iFABXs!bgrAj%D`h@A z&1!teWx7z<^qTO2Mz9eLRgI|S_CQW)cD3F>w&}^Y65hC?8>T#xXV5ctO{^2~p{O7_ zpR>8na)NJ|4%SNx^uhQgb%nd@fObTn2kuzAZ1W!|DH zSo>+q&C6}k{fjAGx+?iX48ZB#u832_ph|7_yUD|kdA% zdOJ&eD$Ccd2gQplYXsk6fdpNbT8?qJBSlxEO9fLJLG`sBi$}!ueOwiJpR$PW;EE_2*!cXfs*&*zuq%MK0G@7VbcABX%s|!Zu#lm z4@@{n^bePIs43e7QrPvQY#gYi&CUuS8~_9hJuJ~2T)xYTS71L#Lm@*yStj(t)+;;( z?!S52lSBnM!8QjhA~OU;Jp71$2&&OPy;=c2w5_48tre-PBVVmq$_>~k67;bjSt;tu z9tb1@q8B&B1G#An3O+XvTYotabA_F{+tYjC{K!dwFDJa}bmvhF9eR1~^JO9=x?40( z4sJSDeMSIkbfZ81+~=tbFp3J#w%jy3ziBP@rP=a}`KxkY+Ww|4sQTK+$o^ofi=C%@ z*b{cbluiv?&tSKrIU?4-Hcy~zn(iYeZXHoa<%EGzc(}xjuf)&nbDxGAbdE&qZlXL< zO!&^TyWf(O!1RY(3D@3I&X*5(TH5UiBw*h@Camfu-pS_RwtByT`SXv4Ex zZ0r@s{sq0STa!2Ewa0&Ee7?&J`dDP(umFJ->FsyX#0H?l9NER8P!Df(E{AZk5I{;9 z(c?U#p-z&d(sTh?`$nJXM{mrFlzqp`k3Ft@fB=O+Jhq3MCcq&H<QnebK4BOy?rklCRmcy*Hny`13e4opk3N%k{R9kA@ zoTKqM%H`hb4oe`5%h3&P+8z~u65(ib!y?VRfc!w&lGi&-jAh0_Q~j9g^7ZR?zuwI? z#mEmv_p0ODT+^ISG(TyVB)YAXRi}2`;UNbPNj`Hu6%+BzExID#Jd3HsnW8FhpZ>6K z(@Vhqw$1qgTgf;Vy;Ow;sBK_J{y8Uh^5NdC%sGjT%x|w*Az$rpP#lAg$S2J+<3!i+WEkL)qeuRQ-WSq;r7djzp|~#^uQG zftTNIv3n(5_1N{;2T0*{lgh5QP?!tdu0IGmaQ87^M?T*Sh|`{_j}5wL$(O4t#yt~fS)t<5SLcb z19jjNdMDGuMj=~b8Fz(|@{*JG&Ue0)+o$VPJkav*`#SQ@!71V?g!JOcD}Ua*UwiW$ zf4vU24f9&TM2Gq!9Ruc^Wn?2$n8ZqfL$iaJ_M(>;UtAY`73;t*b@*wr_#^$~L(z@8 ztZAJp*J3vdpB<_EbeZE?tV+_UxNAKnU(@|=-cz19Aoy!u^lpwe5VJ5_vQzT5<&vd~aeJG&$&P`&1((G~8lUw@_7m-g&7*HTJ z3GwV(_!KQ|7G?#y!bJ-#TzBsg=cz4&%fLj+Iq%ojc8tCP8XYdD+*9lHXn-ZKIbKRe zciB#Cu!B00q5E9Sk|C3asScG2RS@U;5N)je);gQGIW6*Y%b0+)p(aT9v@*w-7)aUV zT=4M`7v8(`WMq?@SMDRJ7-MSTXOnk%(MMWm+mx7WlV2v`D{C2JW>;x*vChX=-fi2= zY0##iwcJ5q?Gi)=p9)4V`aRLoAr2LvAGS8rX&*P(G?^rZeYk;{P2S;?kkgC=W<3T($WcqnHz7KSQC7u zU8ZY=?bPj=B_{H^ikT~;zCbLZ_~I`LJ>_Y%*9< zvnA`A@{ffbFEPr+2rPS+$+AW|mqC=?mCG~kXNDJjE?J9CIhOiZ<-!=J-yI4rdfejl z$(0>pc0qfyO-swb;l4LV=*OCEnA1;@Py72>9~|7Pr%doHmF@jw?%}|&0Dv{nR6Iow zRvR8;=FP6sR0^@H?&DNHSz4J>emCCo>XkaXTF2ux23ji4mObtFA{)r_TvK%Cti+$XL3V=q9CcwkuTlJw{u+JWRg+ zani@%wgP#yvG9VOLJf{^^Q$nG5@g@whvlERTvgmL$J@pL!^wFevE$;oUAr>`d@4w$mEC zpK`vn308Y1=#xx%-{N)T-&ejnt5m$X_Uy*?lZWSF<<e3u(p0%KhPmO0#;(Li#ES`UkZ2RW0<@T=mt1^>+`(>1$-@_mgUJjzJZX{eoIwD7Hh*oyrDx|mZJ}oqk*ACxq-H3{a60B3q1o1 z69dun1NUN?JS^%xHwT7fj6C1hxf%>&JO>M8jQs1f0$iB_>;~Q247J0QwwUSB6Gj`c z0;e)=6rzWuvBoUQ#)Vkp@V2ao44I_q!N~HgXxE}Bi5oHfg8~99@}~vPhGWjvtDG0o zB#*_P%eXfnW5VZo;Vxkq<7q;%Gif6YuTV|0`AyRMhjs-t)u5pQnQ_Hg;Y^u3Caa)n z0owFuG&NJ`eqKE_U*Y~mAuzW*tH_Qij|H^q8=y3JFlWGtD2>t(!xxk$n46jXz>9Px9O3p^%0$#g46eTn%c5je9{`U z%~yz{6`ba$JV%Gf<_}MgUd$V!9rNBv{+nIqf+~k^rt^*_7LO%feQBCDHamuaJeYxqJfFTZw|s}8{a``x!9%+T zqj4+~?=2o_i#%4?%TJWDN?e&uEPi2Eih2IvWjEFKr2>wn7{|n7waRA&x8Yo9<5;X` zd*fRAnhWb$@(u-fEk#Me<7T+N9O$+V}zJ!|R^C>xTb@*w*MjzZqs zzxUloNN|6msXKX)-R{07`-|wEDC+=wi%X z4@=wf+g-6cM2JXxCO^IlzbE7^pLQ2YY>mF_)ijEmStvPr@2~}*=tDA?cD(9bVM0K8 zDq-5%4r|74kcb34!YAEbAZrI06ak$fTMCy^KF*FmDEy(3@N?X z!O4Z=A=7LJWvIni`vhLo&Ojf|6924xKY%E8$fi)-cja9bZSYN^p{B#8msv&FB1%^+ zU$x4(%4RpeHkng7XoAvyohwDp&B6z!8Z#b#H3N`T^&9xAg;7>gI(OK~~F&F|M1(R3{eIKZKk6Zytggx>=mHWkB-3ZT=Nq zJj6bmhY|X}L>G@_@Jz|6k=6bYUEKZuIJ&6wsoC|Fjl!e;5C4cR>c4-ey*vinH?&;; zaD98ZPkf?WFyi>HpIZm})3kSxGELPJW2Z#T}&s5tn=ZR_r>=lw=oj&c9&7!CVc|W*OG%)jgPQ#V4|x} znLuNPw0D!YvMA`BR465dwqy zQ@{=wUomD64IoJO_MNjSzs}91(npc8PKT#?bKj7miu|Hxl3B{JsE&K*fadHCB1-yo z4qNKh-rz82aQNxgu!)ro_Dl5~3PNKu3F}RHVU802S;`jUQBF-6L+v^Qn&ECLiGDGm z$sLp3XJt}$aW8W%0U?^2HilBSR(&LNYeuaLa??6zeWczsn_5>U|8LBcUR8oNE}x!8Z^?>`&qGd`qajlua4a7w)t`fvc(1kaL`3kHG7yU$=K5 zK%y;{x3PkD22zT7A4;#h^izP({;Jf$R0~eAPo(|(VYES0!(A;$G3X`5B7U< z(J&=Hj1E##v>!=N$?TuH9Goe)`$*uzx$+xXGioQlcQZYt^y`Z(osfp=3SEebTM@4IsMe(%;0A^Ebz=TOU=a;##aPICsiaXuL` zha_qrpmJC@eBwlMFBn%|GbnPsjo>+S^3h!X#RQADDwns%0<2n#pN+j$@7msdK+0;p z^3LL&=FIkZ^muE@x3PDJe{4S_v$fGsmP>kqn#nSN9@d(koMhnv7t&C@e3J^>PtJx( zP*u0j0Xh5~JxYz}2#AbBvb2E(hDb_c1{g?`1{Nif3RD|DXm`+>gA1d3`%?jmo2(DS z=u-T3wUci##2#*W;5cUs;r-g79%dApvX>_$Gm*&8%1WGSMAadU9fW5cP9MD*#-th)goTQQ01$ySh{PQH z904j20RMhVU5yB7@xl3>b^(;!M{iEPhHhHb&4nvnOaKo9Eqhu=tX4CI5TSfqcv zj7I!EK8koX1eSx6q=Wki0Mi_^JDs9k6kk1r^~A=rI)=iFKs9QDu#*eFG}(oaun$Xm zh)RZqCXGvj2ZqU2-^egLxQ}|$pfD-8EUFPiB2v$Me|uJLD+y_h^lw7C6Uj{Mk;rwR zISQ$GFdi9|5@-odemlDqHJ)|WdQt5O`6n9YHdf+wh0 z;Jo)7vxYI^xG`Ao1dcf-+ZQ4NmJ;Na(xB_fHppl6j%7(J%!0#u|1|%Lgjzs~v05-R&I!MA4I4@cN4Wzz9@~**R_wLMd=0`}Mnt6&aokQ3xfgH;bFcif7QlRId0^eom^P()k z0~L$CfL>3EBSV!)AyK9UeoTM}7Ir%&IC$yeqtt>UF=to;^V|_guar;+VrCF{Mo;77 zRsJBoDa466<^v3F9Dh)(2W*<};&L=3!K1j5Av);I6+whFG9twJg2vi&Yx(!qR#e*Z zrGfv!+kH1R6+e8RKPf;c388mL=v|6*RGM@!NUw(8B?w5@&^v@CRi$?j1eB@}T4>Tl zL5g$%QE4hx7Jt`u-8(z??Ck9Ru}>k%5Hq|`dYX9O5?FBp&mp{2l|tbLD^ z9wX6dU=umcT`6JIW9%kKb`5kB{1^p*zgHpubUucH9v>@77Y3KmWtaT+1T*oKLi<6p zn0MrAf)&N5AoQ3EJYWinw(=xI>uXc-qFSGKF1 zx3&E0&vNdWay8`&Eqyu-oBwte9T$a$Dn*rMxz9?KWLwa#MMgx4bPqEHFBKq&gw#HJ zNOf`Yv>$1aSQIp!Vb)dU#*hMuisE4KOAe^`1_GkjKzvBBG%6M5Ume<2{Wb&%2Yk^| zFd87=r?7^#-0$1D0%$E*InajZ0!vW&CsC|JFW2~a)zuX1OQ(-j(yd1Z@X_`3lZEF2 zebvDcAA&;@Kv4=IkRwQhPC(;+fSNl%GDT7))WBNmEj-AMCjz5 z(#iD!&Lup@&$ISN?tKmh9kMh+)m+u+iyC(U~U2&EzK)EhOFi(ehL=CRH~pl&^$_8n7=Rr%gd#;%Fs5 zN^?2KK{QLAuynUlf$&@eI@4Nz@+0!?U8)afS%o4gt^~6A(N>sr=w)vtW*j;E%@deT zdA!$VW1H55hg99AQ#S~vKogqB@svlBy#DDs-92b*Ch76n<UwpM-(4?$qV0UTLDIP!OR zX#shXfFAN1!RwJcGJi*kHbTB;CyN&1qtlZ5N9-!}Ipvl-BuuIS!QjVsz*eb%j8uD0 zjwV7&QxF4rrYNB{6(*Z7lz!h4$&f^pGaR7Z zB$CnLC>#f1pdprWToJo8S3-v7?<+3VeoG2<7$^{d0vT6>@j@Y8%JphEPI`3UitV7_ zZ;C6`cQq`Bn+sF)#gS+fOtzVlf_=DO8cHdS)Nx13`viwj0P@8n;pY*4b?<^r#E`bZ zmGbW4SMq|xKaj|xLLkLMewufE5B(Wh8ahv({+t1MVSqR6_nE32Z;6J5jrZQBxqE-D zkshF{O>H#w?51;U6+8q(QTPfPa1|%1(=NQpQ@3! zm16}`ljaN?$)oNUA6$V3lVA9VSxO*mDHS!67ZHj_R^y-a^tMC{LdCqvQ`!J*6l+Zs ze_xc4Vrr5?rUT*CplU9v#`~-|ien-6YRL4@iK!Rl8Od*-z@=jfiYG;dXATnM-XF+w z{4U+1pyMi{BX@tsekFz#G_@!`g~T=OPo#C}hUwzKGWNJT=f9p%q7zt>Ncs^;&(CI? zkaMzEytb8}Qlk?S+-GhmzAg?ye(IR&42n{4K_aj(37Fa^o; z6%*YccqRoq+5RUa+U8+zg%I5Ze~2F+G{O@;H8hp%L?*5vsYQx&Cq)T)g|M?ly*z0C zN>-2^k}W9^Nb>hrLb})@Wj){CpOkj_9wKc+2W6qy)qqt0&gVyZYI)||p=iR^_v$ak zyced(=ITg}ypvjxc`OeSECF|~EWPY)veqNQbaQ3SarvP?mdz=FS$p64#(V{@TJru^ zkkTB(y_}Z`tM?2Fh}KJZqqGbAP71` zmZHtt5DC$u%gtWBtPcTh5_3dR=i(~zYSD|mhy84HJd*3aYU}k6$~N>KQMD9PH54{b zMu<9e>5f1TlB9_r=_91T+G}s9GJE!ZksrJrpq+bsGD;zcD+jl1QgxN0F(o73Tk!Es z3iLK4YD*sh3>j=ek4m7WkI7oLsehJ|dA|Sd3?ZNPkOI6({xJ(|5&S6$WtI7?-mSb>ftky3_! zJjF=J<5X0{_V?R^RQ?p&{%I7q4hg>x!8dAPUv`(Y6UDHQrhCY;RiqKBjT$|K7#K3! z|Lo`cnQaq_`&H$;L~#>21wAia4OO7naz`F6B+?bMlVzUNj-LErJz05#j3LC9@1DFU zXup0bRrlpd`U@W|HCnSZupFL{JTZc`4im%!L6{FW#E<c2Q3E${nPhSkB&(xSYI zje>&%{@f9>`4+Q-zoYt1tYI*_=d1nH-P`WSM2bG}(RXmE2M_P=P-py!d$$11;dv7QrPDesZw8@`5J`9ilg`b{i`To0$ z7S?j@*J4A?f*#Vg&+py&)Sswprv-_NF;r)c5^c6pWKPIK^h^H_kN^5FeOmCViv9%l z${_7z{GE_L|N8PT03dCv5=X&(YwEF-Op-1O)9advbOOc|Rx=ydQ`tDIj*!)QtU{*1 zM3OUsgk%l1qe}#^>hzasFCHp&!hxXV5`JE1YQ!WSdiAtaamqD*8bHqO$~t&W9R%>_ zjL|7aZA}+Z069tgD70R-uxz@3?HixrA|-nb;pr21488U;n5izS(jcGWBnJ}`gSU*m z2l^O9CXA+%bD>`~*(9Q~b?tsXEf_^UbphwFshWARt~dWKWp?%wbc9k%*81+;&(ad7hD~n2t7}~$23QqUC59>&&)+M-^-ck~2*B z=AB9c;?6-|^sBOz(b57h8}Dm-2lcN|xwzLe(G`UT!m2SjGLyovwz9Ho#GHvtJ(lj% z*_Wc_^_Q;2lAt9cTlZW@NmB6|iAD<4s=EkLMg!e$KJHm!vUCqCRVp9DyVj{|5PO(W z{7>AutBJNf+v|%Oh!GbgAN_5b2F?zhjs1BD z=3QwZ&CLn3?tfe2CkDZ0;w0Cw_6#GR+U$ZBgDN$H`z5|PM4WJWr+)%4uP+O+EGIR9C4 zi3kG&4kwpk9;F~s*vFnL+h$bl9AWsIY938rqSc|nWFmb6mwNPUX^Bj?El(4%hyZU)j0s#B9&tGS)xw^4 z1Ria74BX;b*4C!GwhI=uL(}3%>G!YL>ObyCn*Et$3JYGL-H)bX7sU;kZ3RL$ zl>Vx{foCbW^#{Q8$#!ax9h$Vh&M3I{$YVteLrG;qm3z{Su zxHvI9Jkt`^Hc2+EaAIl*3T4#$xTsI1IHmjV496B38oE$ zN$I+)_RyV&oHzwzra6=)PEEEb$%(&)Um6fT>uj)vllaO7&YsI6kS z^^wttgM{mDW%GTiqsDw_7cl4awp1s{_$su?X3)u<=Tyqb#-)zw->B5O^kxvc!|?;4&$N-AjVq*Tme{i;P9T)v*R_3vo@;6Y_% z>a^sGOWGiCSL(5$_RGt5Gg-_qQ6=?@^ok9vgt?O^F2ipaM~;LBccsWoJD9sToYv*t zWZQ>;aLtr-&l0;=E1yIj{lY}=zGYgG>k9_W#OiiL1- zRft3-B*kQqnA;CvC2>innIZCyJtgV`DoJK5R7y^zeTtH=(DliIF~xZ${gTSHQpS>Y zUCbhJB8=Jo#mlyZSJlX<}S0k5?MNv|Q zXguhzy);`REAmGM#!5u-NcR_9C881GC3hahnY|tqxsq74Z7BVQCJJ!D384q-kRR_R zP5B}Q{L4-ft`&t`Px)qyb>0=wzc2L2TjMC1)2KaGvDETypt{_p!Ll1CLL!~z7apQ- z&Fg_OP(A!s4~!O;_YK9f06HY9Z|FuCl-V=0fPDMxpm z8Y6vuY#A1<)m}r)dJddgcbzF~cQfD1ewLwI;zvs5Re#_66uV2`n%-N5heQ3|()l1f znyXm$8=qX&B1bdJ!p`+V;Ge5$#BBR+zR~#BP#--~dhz`n4TkqN$T-?reKNT`m=s-Q zW@NW)Ja$&eWON*JS$*?}o7iY#FX4_Jd_&v*JhqzZ5pmk?mV@LVMiLc6-2kF=s14af zI^f@QHaKsYE`nA(Wq)5mA^FdYrxOtPvgfZLpE+pJrRl96D?SpYBd#e8#;3%C26t`b zj|vmSw-i?zUn-Zvlxd^-Vo)#iedqvn17`9oEX2s4^H335_~ zM?dMg;5aF0O`sz@x;5GvVa#7+h6rkB0G?-Zt`bF^~06>f%iL!R53*wZ4i;} z(TCi3Us;EXyh3_VosBBr^Q0>wP~D~kg2Y}`9jh=qLCsY8FdPdMLX z{H;k(<|HsrsaDYP@-GO)u)xk@l0;Vbn9N7bC*U~@iednw=t%OGb^HOX6|~|HkOyiQ z_I^E?ej{~tdOLMyA9a=pb=FjMwqkYm26c`ebYdz|RKtdf+ApgK@$`nB_r(iNQs+>o?4< zV*;-)5r*udhx96jbnMU~1>EfG;t`8OlDk9sAZLl9l}HODmbm5AI1f^MPg-IGiKGiB&nKO~ z>m$YDNy)MBgb7l*)&;$wnVcp?oAEGaA|v(mYz8j!aU&FTnR&l;$P-5yM_UUUkfi+4 zcWZiKZKDq-^yrdwN_$4}&@tV^(QHE=gCxd?iJYn}j>shapmP1Xp0s*9Sp61*ZsyoK zl78HRzJ|Yk!`7I=jDBmNezV=!X>Hz}B(~>1&tGL-xsz!Sn>gOllh$Jg>!}zIZW&)b zGzece=oioHZAcq1Onh=QST9CZ_?&u(P$a< z%J}HfTEn9sQpQVCuXati*X>^I2GV}edbL;Z>Z6(Q5@!kHSkdRfqAv&OzwbPxQ7$zW zxp4-Yg0N4WKued@r@muu(DUB-UUB232mZrl>dI1pRv*;zS8Ph}aX0ZmjQamp>tzrZegQfqw6#a23b8@jVafE zC~5-fSAwYL4~k;M%xetQdEvj>`K_aj|ECnqQ#%g>UNP_hz%h-_Hf7&(lrzK(;xi*W zm}7W_DaYXLZS^V<3ThX-yea)Ceh;d)kKdp6bt>GFfPOMQRriwL|0|dOO?G7#chT;< zB1ceHKyc`GCN7mRCklzeRI$@hY82vD$b?;|lI{l*U+DwG<~sZG7HH{8z3G?R3LAT5K2-)OIKf*nkMn^;Z9NnU4tWB9sP%lKMC-r;yyz8?KdzH#flf_Y>z9f z;xPdIUV*u{1>l5W}ibd zV`9#sR*W)ZEVHbt)ie!^5^s_$VQnz|OuRJjQ6R&W@o6~KV$N!B@>8jN>Lnp9;(>3DtMIt9H?Bz&Adhr>% zWaz(AG~Z=vJYU&4an z2EC4gn`8`+mJ^8V$DsHf{uo|H-5)pebs-LfXx72<9qw8U&BB?Cx{u{*mkW@_(SzBv zpzRH*!(0^Dj?A>FN>N5f zH}gn7iR3QWXeS4)e7cq~HLA;mD+d*7b^2BExlfzZ)LMC0w)7OA7ch#`5Umzz@SNmX z#!72Ld6d$|z*g;0IbFp=;LHt34?RaNF}f5}w|r2Q24kXNjA)Wcy81g$LPh2r+L^kK ziJWmJ2{Bl(-yJ39<~SJ_Vt~2`et?dxktp4YA~JZN4g_zK(ZE9|A9R9Ri{D~9hD7T&pdP~;G+d_J+FjRfm%{rL zOv8~bS1bfHJ$3#5?pep3=Lfd3i7ah86)JWV4mNjOS@(=FSl*Cyc{&`qtDZe>N0fNp zIsIoTpxj_(--g8}M12`&`y38Z``Wz*e-bi6Jul!B)3eLu7d$t<`mO7G?_t`Lhz|zu ze)0S2Y(N(YuP;hQ8X6!-YslI0z}!&+RhFh13^!h<_yY*8P>N zRp)vyuX8qP(JB|AV}b~t{rT2BWvFVTDDe&>sF(Ois&3Pp@@nvpx!rlW5Ti*?kqq{C zeN8qEkN<2s>R7Y?kb}ygT3#p#O~Xea-qdB=cFP zm@$r$qh}d7M=JGBIer1kow>}rLx%ZY9VcbvQzyYj`coQC0{S=sZXODfPU#8rrt4Y) zYw-P61K8Sz}M&!AAloE{F14PtJ@8irJ|*?)hk-^Z;rmNS}JqoN@?*vcoi=2 zQt4OYwM*y93!o2rG{3(3u{>F4T5&%_a1)nF+KRD%9WJ_4*2y>i#8%$;qi=O>>(*QB zjb}a<+4oZv9N$xfx>?HQzZEYOig;A*R!R{Pofls1j!*p_@ev3GV}rK{Zi`a>>nPV{ z6v7VALjDG^iVP}rhc00$GoQnk)I3BJS$Ob>@9uK`*~ZGVc=YqoZDe$abTq~-S{Q~E zB}M11MN0_-GP*HV!7)l6a(ZVmfLbhog1?t?G|#`Y{mj>)+E?r>1|=MAF61i*18%Z9 zyu*cC#KhT<;!F}_cr6gx9@uv{#2s?;I=B0i^a$1jghoglsKb9#$6)~ve~)tBY=`wWikL=xUT>ox3>w|P!D@1QdJnP-m_pIsH z&wRE+(obtrfvS+P=MFInk&#tie;33ts=Zs8Xkc0PEoF;P?hu%DEVOM|6aC)^)> z?w^%FIJ%4A4n;iw5apbZIYtrq0dSZ*OFznYTi8t>O7#5&OJ7v{{2?6 zj!U6X@MMu|gi{U@GgUit-t6K}tPtuVIhLcIw@G-ry>2j-pkhsn(^bvSMzQ=AsXO)bMM z-eglIWzj7P;#{}P$O3QHo#Mg0qJnCd5j~&9zP!m}w+WHZ#iZP<^B?xf32Cf* z1UcdSnIHDgSc2l&@SoTm(mabWO!t>XayM%8pA{5pS|zsb6hBQ2!n7B)dOqx9D;n_3 zJZ64msTiLwL;>%BN4r54cH!zc7?IV;MCC@jKt>!MeOycwz?J$R=HT&6Sg!LjKCh^{ZngSj2gNWy zx1YsAV`U%K%DeR^G-sWRI0Dh2PBi7Gb?D&KmRT>s^)u4NzFYEwT^Sl`{{&had8=R3S#*=wq< zzWPD#2vuYMF19=of%xXRQdi%!k8QS*+hC7fp=?NnIru%xToQGg4{J=Zky}Q_hK=TZ z0-8eTsH43a4XUd^9Zk&fjyVg?Z@dr!@!pgl@wF7#oDRpuwPqG=hXgz#_Cx;ce*O1C z?@iI#`mmPG`IbLq&5NjJvu_Qf$xY*7^=(wxZT7}q{l?Gd@0>G*tLft_VD7Nbo&HyU z*|%U}$6fIF>3c(kZTi%=v_C$|A8A$l)w;vnhLLN#9^SS!-?m%Xel*lRxeWq$Ux9o``be-}&{o`@rQ#RP>v=NKt@{}uF(!mnSujav|| zRODb`E9Y0#Zl;o#2eg}twL7SESf;dH3%zGh)LB?xHrLVls2je3L3n)V?CgXuPCu*o z<@(S&w7#gT=~vgwH(kxP4e#7LTT}dIaR@|cH>F^A&#&%2jvk^)&!BD3aCi@?sAsIZ zXJV=6)vumuj^0_7-q*Ih|BG7`q=0?;{)bykeT2H;kp{?`Fce}a zheNTKrz#F|7|=;~_vc?P0YmR=STcb<=tLG)u9*U4*BgD&F}NT-YgEsc`e-@(pDh6v z(-&X~MuYF7{@stFxm6=`6OaPY;>pp)#I-oluLcg{oDRlFa<6Twzx71 z!;*b=>BH3zxp5LNtl8>U2u;bwBkq7R2Bn7qfWBcaEEqKn0dOF$X;6Tj++8&wm=kjG zxCJ!;LZ|`c=O|DU0CPX+l9z%?;vF(oN7sY?V}7t?%!i~$#l)I2Fo1%#nj^FAAWer* zJ2fO0NglBzPOCN|t9lch3H88{XmEZB4WP$8ox%Xw2Lp$foD&c9S7!a%&vPi00MzhO%HavM-ztv|Kaq zgZ>p*LP||~mZv!*d)duFH*l`7{;9uqayDyJWvMJQ4<|1#i(d#xDx+zxhea{x7eJ2M zK`wF*lS|RmJnbnrr^eO$@gvm(RiuRglHXU3d(96xB#Lsu_}A=Nq$qUP?1kVk>BiYz z&ezvNNf%@Y%LO)zVYypE!SG?;QR2MTqZqS9_wA6SXqAcXL*`yW%kO->y^}1+m*ubmA7K_pX5MK&eK+A!$H1l07A{s z`IE!Pr59_BexYlC&b0V7?C4tVs>J|^|Ii^FZf=8mDT^YTbb7x>wifX0z3_?yH38a$ zfqm#*9S^@Ko`8fdLM)i!t=vAEM}F9CQpgO&HTdxAvbYg}?5ua*ViRi7Y&Z$s;7xZx z|8-@Mf(iW{+hYQ2r~wU;>m%W$rw6YUjT_ij#X0{vNPKqqVJ{ajy>2eMwN<&rE#D`% z?7)J8Iua(XNcJdS9bo@F0K&6GAUQ=!I1P$9jeZ+r#D_+tXU&ZruDw_4cW#rw}1xi4)q5x|;i8|Hl5qlOyXhZ790xJQgBw8_Aw^khB0jEzl^w`F?)P!u0c>a|DQQ{urzJ88p?6_zZ1Q+XI6v|C%9pduBJ5 z#Ty%tO6T^F$CTT}hd(B_RJpzg;lSCi=BPh?um}LD@{nhZY#BeC$GkK#z43LfVRHHR z`r14NI#Mo8^@Pv$+sbeI8lP^xFP)aRDJ*b)u+nXi)akDHXMv&r3_W}n$_(46-@oH8T|UD*Lj{#Xn^t<<)=o^+O_k3sId2H{Bl zhf;L1rx=NOGvE2N;$L;|C;S1VHgFjy;0}s~0E}0KOOSmrYWz^e1`}i-2OCfS5=(UY!RJDpn{d`O_f!!5;bN?tFV zUXSB<5zOJdFu@uX`dpH_^salB&5lF=xW$2Wo9BF!>(ZM~ORW?p#%Iw2efB-F*j3!N z{uFmSg6DLyqIsOtQnsN3z`vDt=*SefQDwLIbb&+D%?!r`>r=0$HMz?FI}qb!nR=;B zy^#gD^%AdhX?wp8aqcctWg`1`#_~q2lF8wtN15z>`){Ovbv(Vf49{zcX*LX8Sb9Pe z3p`K={;Dd#K(5AnP4L$0@6gk|`6ur0SO0{c9c@ol-wz&izRH)19K9+(2t!CR;MBm> zmoW_r`T#omIfgYVE$3KPzw|eh*E<@iW_2h=fi@$lQ|IkV5$Ob>_!y)ime8P5lka+c z;MA7tI+JN(AEl*+3QmVR{PakN$5#p<&a)mOb@{W+?&t{?sitIX%P;aZ&X=Inbc9-8 z-!YJ!8;fb(IBhO5kQu*xG9jtC3uw1W?&1Y5Z{`HNg8YE>Ybl?+4!Cjk*XhX(H3(B6 zMuS@J6r;spejaF|{hzD_UvA({eX;h_n}+5x{ZLieucxLM&3V)4STEth#ChV_0F&A) zuK|#i;}s`ygf4!ZT(|)r4KkU5!W(Q}QdvCZI6bSgO1O=~zR7pO6Xh!{QkdRo3>K~> z+f`yu>b+Z6n8jTSB*w`pCNMR&Vr{N<%K*( zKArz)5M&iD6Hi*v*x#?}j18%6G8O$LA=Cpi@n~<)jz%?ejd|5l?;O%JL{MOA6jEP~ zh5Kz1_cwhrM%nqJO3umgehG*h5wKnsT2-I1Ng2gkqL)1Y>O5$HBLjJ(JSa-J87lU% zF(aTanjERy2PC}u{QHE7gAkn}CaT*f68!+#l16nC>o}2Otc!{=Qf4OwOp8_1w7t2N z(Yw88${QWWr+w*9w=&GQR&bjOe5=$KXl8HV`D{OyBvk^b=Y8MJ3Z}Y5jOGD;?6K7U zKI#1aSB~5R?eTs5f?Eiyy{E(7BM)?dydS|MT^y0Or#1e-G{_lx@Br9qo)U&V2eO7F zoNP=<>YRt_l+AU#k&ce_lKK5`a0xjL>wvg2AvT^SKJK^1zvnrk5oTn7MB#NxLtI>J z4<<$~0BdWQsKNA^;4Zd|Mk$~5BSoR2dOyaPr>BQ5-Ft$&!=dq>@eV}HP(-{2(#O1v zBHu4ai@hDoXZ^GKdWolNA_k}a1?baO<~~3~obsXGvyKQVH-jv8)i?{01pj-jexU2*^lI*B|8)I~Io<;`<<27#Il+l&F z7e{HeMwE*pv^S@=7DE+<<_GVL&>GF5ZA&V6<~@xAX9G~`-9joeloMLK0%TDh0K^HI zXg;|CrH%tM0{HWZsc~tKQ;R(q?>vi@JcYt@OMgCFl)B~=m-5(<1mP_-ijz6L4VaIs zi?ZimvwGvij;elu3WOE1ML!Pf-AK;p9$tt0$JlL>0% zdfylSsq@NLfbwNplF;|$%a)=~6e#yJf5|+vTUMUw2sCP1>gz?};ZQF_P zGT#Q*5-S36aWhar{L4cov!2=4^L4S1U!O!HH#y>{4}$BOYU=KPDzg7DsUA6gw4$wi zOvluqFCtt*WDx&|G`r0Y9yRm1;g&^5K}{wY`iJV4_nqwG1DD5f9+Xs7j7~7V1#zzQ z&(~9V88{3=E}9^jT=!&e^goj(`Jtk4dd+Du1T@s(;`-ljNX5)EuuJXeq31_raZNDn z9Ah?tJz&ys;mdn6e$7E&w_UvRxUgW%u^&4FL>&=>V zWHdQ;>GHM7urDFH*_q75rZL`c+@WbO|3p~)M&>or8B~xt>}4sdP|?>US79xR&MiT` z&SJ~dBup|R{Xbp{x%aGPW!5Jl)x%sABcCwn9X+57U508zg|vG_)BpA>+PRA8nz#Sl z+Se`4g8Sa{(bqTPL-!ry`QVp-{(Nyb;#h+Sg`D@p@8M#F?AEE>Lk771;u3Z2HW;cy zh9vI!r@Gs1a(oCGx$@VakYKmPCloqrbni}1wcWOud+50R-#hsuc001wp_9J%0*XJ_ zefW2;IPz~m35ETxrcl^S+P%OEA^SZ8_prH#e*u%q&|44p62KvZ6 z0e9kI^(ICQV}JE-Ldio^WI-Ba=23&I@7||t`4-DXTHign)>VzdG&$U)Vl-gD6<3=+ z&QnY@O~Gfi)Z9{b`&SFCMtI0?&v1zA^VZ*@dUqqIHCG=F{^meOk+qEoHTG_Bgx^XfW1?h zt40+vs=`vx~3(G-#7RUOQGW}Y~hr-%4kUaWSaLsU>Fh2EWZ367+@J}jDnyUdOiQ0qODPDLq3Xs96 zCI?)5SEIF-J*;h)J1r#=a9N9+ZiLp8by#Ic@Kl&_ZDeT{R=7bI7F+XmH1qhZ38F+Q z3`;ZOtG^mDFoM;U7|lD+6_xOehuDT%=!X5wv9TO952NGLI=u^6A;qkRQafF3Y z3};&ky+H{lvXsW4rH@oa^Q5v*_XIYf>GJ%Eh#?JUv>`H)ngE(Df`NF|o0j!hIjEws zExtGTIDaL@B_<{%8Upzfi0)*~hJnP76C5X$<%$<=W+ooG$)RF6GroPN#{Ul3`=wLt=}4wbw*|)QS0Kyy?hzRUZkxpX#hW|k`JL4%h=t@;=o5w zvPQ=%rVd)sWIY{51hlQ+yvz(9(icmAF;kjIe`5%nidaH?0LBx&r}}1Yq>9K{lA;S! zX$SMC`k%_eIw!g4^hfe9n=F;uf21ShN?)l-+d$!)vyCB>TCbZiBl%Gyz%bX?#5yyFK%|FN=H1sxfH|0M{)TyBG1?rYKK}%1Zr+}1 zVMJ*mXKq+&#&5zojApQX$SFB$zIh@*0&%je&4cJHdVZLXQd&4)995w#Uh2JhjkOrZ zTYU7la7p8It(@mKLH$89`1(GP*t7UWSqKJOg3xcD>{xG3-E5)pZ z^YBlZ_q?iQMi=hy)29bjSDr0oa1k=es51qGBEXAd!q)y8*7u!&2koi(e-L@T)djr^ z+?}G8PIZzq*3IwUKCuyP`CVSZCG?o-U82Oh(knLQ=I<(oL@TavRl2?_O{1y1E?VRJ zuA$MUSc34*Y4DzX)|1FqNw%dW8QWGaLYw*3>Ey=t-qh#zRBgVtOE+!1$ZV&&mS&Qd znzrd6*-zYE(tm=NC4bnKyHNH2xir8<7$Tz{@Le9q+YQED`jxdj)@;|Qw>&m)NBp$J ztt2ZZ%P_qyBg3{L#wQC%t-Q8xo#sN$bIFQ0+oy!sgG%i$*{(=@guUyfS-NgNzhdt_ zwL<3<-Y(1Lu(LtGIe*#OVAY!X8Q9Um!PdcsZ*@mP9vro5W!t`&#<|OcJowbUU%C1j z?!awr62!-Fl9qjHL>U^M9Io;_P=)YyQvPny`@1Q(AT95M%8S1DzCY=u`Qv-*><`Z$ z^DI2@pFJTd;D7m}qyOC>O_#v`A962-NnqW9e@pcj^S@O_QE2vL46YT+7o!wT#ie{Y ztfs5=KTGwPaw;bOk`frFs&n#!=!us_tzKy&pdz(Pe5fEtCS~xf(X`_Bf6f2emtJb< zKy_$mP{rKK|Mq7|s@eV;Bgcg2xIFq@QQ8;%+pysOn*Y7+)p6Us>oMgwbfWGoygd<7 z2gli`l`TXv}R6&N13{0 zozTCf`dur$g>U7iC8!t#HsW0)m89E72H`1fr7GexxN6YT&YJYKeOi|obDRJIaHt0e zs%<5Ppn=0}44sCX8Sqf+a#il7;Xv01(q#*7`M11634EXqt?37vNh8&17fbaXVZ02xI9oD8X_8NwV}TWh^hE2QC1j&XL60t;#dxdm69F zrw{Vn5tqGhVz}GlHHcm80NTqb#IsIrp0Y0*97VtQd_c59vUYvD{@`M%p4#G>jEFiz z-|P5aTkTY?-4guTAloO!xiV)==19Hq>0a>6N3RF>pXx~tlY9C!c>N4ZMK%afL!Z99 zSgMy%FXx6z8A-oQns9~>W~6zq0t|Gpy%txec1Wsgux1zd@`z?$ zuS8A#S|cV|;dfx^hYYZo&95>Us-fjhn*QLIce^X~ z?wKlq{qlj%G&S!8a*H6_`uV3u^LY9ud#s8u7_PLRdAi0Mp^W+5s#!_08DVE~(;B~o zu)lFAZx*RJm~(-n|Fcx@v-MLf zSLgYk|IWSqe_5(05t+LVKc~O z=aB{lm5-7zsb!4SxLy}=fDOj3v))C!dnZlz~55scyx1?eDuA?+u zYB}cz-4W$?N9iFG<=o#%BUgVOW&9`iqDRtTKPDs^R`9Znk}m!{WfD>=_=WUF^=*!` z@;O}$WJIo=p^hmig|pMQG?=18j`h+u^Q;v2P?|%oZZ}l%@Ak9udTF2$kfd97uckqi z;@;B5hTC>nYu(NHp?nuMBSF5*uIRIysBl)2ETowtajjZt@O0x-g%ZI7K~?}?+)b4|b&82iL%*mu(IhstUXW5C8P2_*Ux_>A zCF^z3Nph0%2{M5?=GU5AB@<+e+XR)BV-&WBO{=h{C~&*p7iQ}OL?DWs_s~t>G^|GT z^<_r~#wdy4yM&zKIl{EFA8Y{gn59euv~ma-!G5lvbdC(J(g zsYU&3rc$5D`dPs+LNB*gP``NW8hN$Vi)Hv@2&mZJPv0umZtelEhd8ENG<#fss1@q? zMw6vpV)7|HS{jaL*X~q)LyiyDXNWpvk5&yn-^c*_W&4tGw&%X`8EUQzl2HHsfZ!H3 zCe3@;2*ZEOFZ=mQ?1ijPOo(mII@R|!O*)>t)W_yuC4#!wB1eB^v9BJ>5bp1o^DAHY zBXHC6-osDt!#6Md(L0t@36eAP^Z2db_GK@{iZ9`P?@(%TZb+WP0YP#*B&gM&^4pc> zXy;iBybMe3=xZAXL9LSwxD4onjx@AXv*E!DGV#bn0qBNfDG5FFG3F>)NR^MFHJ@k% z^5+#Ty_imlWdNi7!6)NaxlR<(SFged5R^;2E~}u9pnf)_c}s+re-s*eN4+kUpv4Gf zxYKtBDEsXy@z{96KAx&@T--JKpyPbx8bStHJ-Rm9#+n$6aEw37bLVdQ;i_hg3;S!B zbCqX&EJ~}bgwcpWd)#Fht{PiGyS}SaR6Tp2@iaA}!IVw?C#v{GQifck*3GxBqxw;Y zxviYK=G0mfZt?onxnS?I*UISpg(n(r%HCC~q4b6RUvFZ1k&e_AZ*S9mYjj*jYJ8Y^ zUGKo#mOXS(W2PcGhrqWcXt>qxDrdg`Y}t`5^EuW``HiOZ37S6Yb|8<|>=h-gXL+-q z3Oa0MwwqdCe77Hp5Sm{2jrhOJ>daYPRrj)l1}VO~&dq(~CBbQ3%cLv+{mkB1dJ90; z%noJb_Goj~OX z)7F1xb#hnnXc{b^5l>%2-oII$Kl@fwU2mRC(!0eaUnTE18R^v5-VoL6aem*Ks)dg& zAmpXF-O^pQt-I?|D`_H@;q!k~_a0tN=v%jFDoLmby%UNMdJ(CLp-8VvlP0~Rpi)HC z5Q?Cb1QZYu14!>m2azTnqzO`#j&x~?0?Lc-ea=4LId|Xty*tJm<9q)=M#gVs%{AA` zTysv!OLSI_-;p`KS#B6PFeWuQhmcfk42nNGNaAP*$)HM9nZ4v=?cT+;`_%hzlyQIT zp$+;zdO0ozF(vi&MqYcvRyP<2etqNP*-2a7jVne+lT$yx*1mnU75Mh(ycNWN9n`T6SOX5=HBw@PGD8Dj28&l!<&D4UaX#zg<*_9>&Tb-H5l ziP5_?67<9m=u@9@`X;D1H-kU0=EtVxTC_LnSegW zutP0Pt%y6D6=7gtL~Wt3D{984EB2I|z`@z_$ecjUGg6R}JzMKg{i-GOGnNNRnXm>kWJ$hwuM4s& zU%U(x{L0Cu{j_yx&b60X3~iR0Z?qXZF>Kv%h7UQI*3MfCIRUoK$}h5E*PH#{r^HUy z#!kPFWe>G`%NjSY5VvR5!x?7tqY2It zv=^g3jWgH>Y9wUcg{uY;f^N~!gRi|ujvmm{Ju%vfih!bse%eUp%i2Y5On}Sx&Pfx` zn-Z>O66j7r6taQy5eaTP3TzHAS9d0MBub)AmXDMWwg}TW!Gg&b%ry2qQWhL~L-*L- zksX1=PtM4-#d|UW32+AvnjN0?c0=`Y!(dQ?f;uuGBAS?kG(!;FmO?KJBd;h%J1A}>JYvEg@N?EOsIvjs0*w=E)qEz5l;@HzI+}S+=z4c2piuq{Ljd#U zaF{gI5ug{Nc>0XL>D>{rcwuB>T}rH?juarp5Gds+Bp;D~1F?e_Y=5Mwh!p)o98ZY! zs5Ozwyf3PVD$ofizZ_I0tnDI0VT2DiSc#Xr5pIDH1a>74qi8`+&4LXjAGAiz+w znkw3jtY?&$KK&H2lYaXV0EUIA698$^f#&as*H40oaw!;32 zGK+geI+HZHGNm>F*s8CAcLv5wY?O>atQ~E}SYIT;@=Kf@t zW>ehQhEgVZhDR z*t>^$IQD$JQhvBqeq?ZdRBHZ%`uv!a$^5v(d;)txf>OaFtAeE9f|S&P)cS(-$%2f- z0up;+mQvw!tHRvio0w*by!yi8!h(Xy!t#20!C641ZV_E-ES)g`sR}_NVHJ}FWKlOhXX0f(oGufT>j3&S3=rmx4Y3Q4ii=R(-)6_=1NQ z&Lu@QIxy42qU!1a(kI*I*UW%wI{u4ms58bD7V<#O=Avy+dNsXDRA_}jO9i_hNW>l} zNPrmGLjbc7bHH^q>>ab)ue;)^O*cB zp*3(4feIn<2unerzChY$keF0)yBClKM~+efpmrLnZ#R0}&fl$X?B_rwPyw##LBxC^ zWV?dk6R9$NV_2Rj%2+t<5&|V}VPLp@QJeplW`Uxi$!xtz4)A)H19c4zM38I7xPmM` z!ce_x>8%ayg)$Qx$JQI0s2Yc-00j;8HM;e=RH$?0PamY1w?eghHKO%e4i8bh+f`f@ zwFf5kyws?psn;jU4OO=r>Nv{c(kff)n^C~lyxYJ7C9ndP;(f!*1?why2iRSE$g%88 z+rg)HzU=@)JEFXu57(~Sm;Xo^MHUc!)c_n>fvKe^R1zT2LD>BUsEI#N2PdnJgP=)J z^=*>P#v3%C!$3dOQ>xMMR3{Qu{7Y(t25yc+Z}!!WaO4N~b>3|>3Ml~GJ*ne2_XmYg zo5eeINXVnCaTJUu-QiPYxd6xO=Lp2R(r$Y^t=<$kCgIMy;v)6I9i6Io^IopP;7)DCh29hWwa40G9 zHW9AjDB=Dr|jhM`U!oo?Z5Qk1$Y+STl-uq9XPMrda; z0pj#yLN0D0gA1XYu|RB|bmc(FyUjxS->BP3Q=@?JxP0xZSp@piB{k@{ zII7fUhX3}`MliTRe=O?hn9WJXSc}cl8pkvMJBW~g1UL6FOV?iDMnQZ~7<*9qRS-Wq z&D0)B?TuOvS!Ir44N*avs4PX_nYOyR7JrrJnlCi7eH2N2#fB;l^}XRFRR!CEHEylj z12)VD*G-i-)INR~TN~AuKuuo;!t6T~i~+|2B~SF0bhuaO1HqpsmtJqqWHG(NZ!CR) zFK;M)aQc`RWQ#g+D3O$hY|+lrZNtbauWlODuHomehNwFv2%l}HN9pGL$9HzK<5t{T zfaXol{*}y>Dce_;r2`j8{yWsRJ4-&4QqAKQ}$SnXCaDW;qD7Gvc@$It zYxF$5^Z`Q!z!M3B7vD1L1=K13@*D~(JK(2!XE=KR=FaFN31F(4ZCG(!V>gO6Z;tt2mPUPJ$ z9fyZkln)<1`+_Nl>Ac=IIN6VmJ+k^dQ_ph*m<75@Zl8*8>8Aq}#!ehL6exnJ!hqE6X;IP~9`sG>8y%8ET0lQ0Q7DP_SSs3?Z`LoR_d zg7G2xUFi2e@2Gy?M;OAT@n?U2VpnT98uD`eVN`ENyv{r(;EhI6N}(} z!2F{dhDz&|dxmHK+xnwlb}xp?zx78<{-zi8&U`U7y=3NZW^V2O#mv&_vA?;MSJj!( zoyk#ui*{Zb ziWizr{v9}>-`>fOjC_)g3tOFjb9E(aDJQP>A%~JGluBQF9>;M&KISB}%m$E0#__*<2ugqqNJH7b$S)va>5Qc*@ z$4LQ!=ad=%GwEIEsAwe{fv!kiJa^O&QdN1DgAkwe&^1-p_Zs%!S8GnTjyJENuMx21 z3_ZjFVi9I;d3o<5x)Hx!A_NouxRX$Gb_V!4fQNigr?htxM!C{^LFKkON6!USSo!R& z=Tvh|d^gYEKO2WeGtL3Xf8dcaNdYxs@zl!Z#5D(F8S$G#-_)YOdsQ^I0oZ4Ua7WmA z)=UbSqh2KaY=rPYF_n3H7JT^;z|=)jJU|JDs749_NG{ZF09AU*L0G$wD&%%8mi|}{ zB7nnEsh3BwPpovj&JT^{GH%=6&_KbE%>a62=;L~{HWK;5Z#dgo16iQ0+2iowQumZ# zUKTDP9ZQtv7^Y)2)EHJ~%vu=DK zo8|h>M>A7&g_5O6pTeUU7!hp|-;jV^S^jhJPx@T&;dgFLJGq5K_3LjXrvI=Z3Y!$@ z8f9o)^Y>nY@Uel<$rfJ+yl-V}pS>R&u%CT%m+ZMQ8eZ@mdd*)q8=f)95Tn3L%Yp+j z$r51f`e?&G)iK5^y0tO;EmRC4mQOLQICwh(#E$GYK6gnc--;fj{UehXJbPd94h~|t z4MsG2h9@9N>Qvewu@%0UbXVH7dYjWFFA@<0W@vvXj`im!-ZKIf z1sZ22xkk~f;hg@(dR!Zq?of_^X8TV_E}QENTjh~b8}5>ti5nW2qLMSyds4{RVz?lc z-s|}rpc`lK7in~@a!elkJ>6t=dHcYq#)|gJxK~10*$$*Y2sqH+M zcb$JoM7jqE9eHz!olTpC*1d&}1`eLC6|F7b->ILO!|EVSO_faA<*{cZ^|B8?hN#)U zy6`6BYu{~R>DfepbGfTZ!Jz)FAIYhFa19eXD|&%+Yzq?0*+6+V9Q+X7LZvwkmhmHK zQGRVQV(MZZ^*8t)=hB9)+UhZG4rPyF0l00hvyIJ0R7*Vf6}Fpra1?+PXC1`Js~EY{ z5XSJ|2pwRDiU=yuN1moJ_q7wQUIMh$@yBj5deW&2nm*W7K|~{bg|8;n^tFE)6Q<7+ z#b+>vF;^)h0_mebU8PbHVq0cn?%YzdgUWfgfRAk{%Nas`gb>b-jHAEyPhGG_;N)$1 zfk9^QnMFaQR#`aZRb2EZ-HTBX;`%_bOj$tj^-Cywr4jWk{1y0h{g#ZM1$>Fva(|Y|VGF_rn7q2Db-t)tz5MLhGcBTbm_v({tb-r-O1B{w!WrXil~1bfMop!rsJ~vN! zVOz&R#_qSh1Q6rUFb~^>NUCEj9TKNEeUJ72#xRNCuSVOx00|O@bH~!Ug(P(H2~~D> z&qO7u>exAgMN+1~hZS|$4H7KWZ@(2f(YmtalALjtLBP-+8!TuKViE&<$>V5Uvpz5N zzNjAArP{)cIZ=y5Le4|5B=|h^vqUN9;vOh0Xa_uF@cIgFQ?T_LVc=baKoBspSF#HX zQUxt)zupAo-;E`d-t@cuPH05vUgkvA(dUi1ty?n<_L$u}uBtZ2qmui}uz}R>+hMYE zDjJtUsVdmL=s}eUF^V7nRF>H7&dQsTNFl&b#So?7@ldg#3UAz;OUmj-hW6A_(}s&? zN>NcVhL++Kj6|svJQfs%(&oxly!ukn;7>!)kZxmhC6?#>wIvO-+_D z($ujh_tDh-GVu0%)6KZ@+J2USS)T0L@4Wnu9a?-5}LCj%I>HGrRqJ zeVM|b?aYKHF)*MRsXXEUC|dAkcGlx|=Lv@G@rU|!ECBM%Xqx~g|KqEZ}u7A z^XU$vF3dc6gV>3Wh@!pkj*MK>8{UMDeRL+jq7s5{46p`6-yG3~Ik+Tr@z5jNV9-rDy=w4-9Qqtmq?6l*_h)IN#n z(~h0ij@!_VKh`Et>kv7062x>8&+0tV*LiHCljN_A_1ZHi^Nhedm**_rfRBNqPEBam}FU% z=$NK57}naJFrsfz+LBrxoj`>I39teg%Bi#)*vo_&AmIt6FLejnx$oe$Pej(i!YOAY z*apm8C zuo7E&elZzethrPYnsO0+ON_K;x||?Ruqa`%9xq@gZV8b7TGyYyS&rA z0RAc*LxKCHxwG<-pg=PO%&`i+los5JadweJTq&Y5O1@PspD)Ps(0TDTh^=9>#d|kpShGa76zyecTqjjxFpN7Xdd10RtJ>evo3lb z-3ddFhTj-YziolHwBSLsZG_gWzE?-1E>qr_xakz<-(aS!H$cUjqKFMkJy$~(*)6b3 zWm&(sRIkJ1>Q%GNa~qTZDWVU#qDp$ZubzVQ{~+eJ)hmz`x*tp$pPNWJ2Yd7Ufmb@@QZSFYpTg z)bKk&U?Ywm8z&zGEFZBbZ$4WXYCDnh0bem&1jdMiaxa$e(Bw+WH-XTnuwl2TQl6 zIEt=~K!?Fy zz>)l`?mV8ASam!NTnI-xNoNpcRNyjSSx(v)iKpeFWF=T7$KvFg?9W8@g%&I*e4Kyj z(LXJ~pmMig`uhS)Q-3LfLp7JE{*hx^cJTtY^MoF)(K^MH`l351PLIrL?v1A%au)M2h^)cP%qqFYeAQczG z6mte0QZ1 z;2_ErTw``)Q~jjM|0&_38{F@cu+yh_yxS(}Q#H|z(E90WT!y_tsRLKy-D)$OT&dL$ zw=lRn1$KqaWF-vi9vMfxF9E+V=zij0>;B-YTX>55a_h<+m9*Q?mww!q1n$Im35jsb zsy23&&15wU>yZ>kOs-}P{qaH~%>!@Xmd3s6&%Bztyc)8(V*7m}jf)|Z!VM$r8IJLM zoyhef3v}}AcI(_`95Ezrj%msOr3QFgUK+a;RQFti@2#T zie9z0Ui^Mujpw4OBE8ruyu$Lm>I}VZOnX%)T`E7V+{nBxr}%N^6hm8Sv$T%)0WTJ| z|AE5Uo1N(W7W^ud;N6o%>?l?Xc@+mD$Rp8 zXVCWC#7&-^mBUrAT{L-YCb>s82~( zIhKjbkym}|uCCc_Z4O@b+?ZouzPh=+wL93l`Zanl@*l!F-y6a{xtS_20h=$D3a(7$i_Huf@TAm^yn z-dO+G-%#^;ra6p?$FQ+>XQ3-m^vXbE-QK6cY`tfOuj;?7zklg9Gw`b6;PY(T!xJ8( zrp8~wx}qy@n_e9qY%laaGkV?hH=bnGHi~ zV&=jb&E4lBSY7kyA~^$A=I)~-G2|SzB=`Af#z=n@#i?5WhKHDr6v9Kcf*@3^^c-d( zj%%XJ$wTPrBT~F1;`@BOD4PeGc!4jPArXjOHE~!`6?U<=Q2OqWWTo4&gufCX>}u^0 zSAa&a24U3zoa8l`vrB3;8X^4L7etB1Z7)0d^?0BG0x8CBbe!Kj+%E$jHMha<%ZZqS z;xt88pFcA9T+O+Dm;Aj`0(ug2(}P7k4HaTMMR2C6RknrnYQbwoQo<)anBvM@4K;*t z`CwZ{RsA?)QPj|r)>8CM9LV|g#gq-*US=0$c^PpK3|G4&1uA>xTC`a`9k9MxGaD%? zp)uHDx>dW9TeMZbQN6y^u-zg0xp7}4-}%+I-Mj_0qviF_uT?&z5t;#RdcG}iq2le< zxi59ZHfk-g9r7y8d*=&8=S^Hyh6e`RFnSfBr%Lt6e5y+I9_e9`K@mY1TBg0i;u!M13mz*Y|+nrzcpLe8aUN{YiTcbV4?q`W2td^VcL78 zYh{wmw^eg%&fM<4d&#Upzv0tpgJxu6#=FnQs4FiXyyoB`?!u)dLoN@TzhbvP9Iy35 zZQP>l$Jgn=&;Q6#i_gE+{psbSJ3$%uf0iA|mRxDNxqB%pxW0y=z5dDfnX%VSCq*-+ zKa(q_eJw4?I_mz2r!LQ%t5}t}ou@n`H6IZthw@Bek;RHuU z#*jU`k1bzlipF_{SEeC3-!LM7OLma}5I_tIpaFOT^vKAv0WgBIYL}B*!r%-dHh*Io zh*p2VNwy5^PW;~>3pR|BOSdwAC|mn4$?m@)OPPJ!11`Pl;)$2;BZanqA`2dZWdXpb zLC_G4DxbG)gYd<4G%!p3%4>g}aQN9;Z(ATQCPnJV5{qkd7Jb-!*8H?vMF!V-3gX=9 z_pd?PX(|SRv#vcQ&_-sO<}Yhy@%%@AgjP5k01ZN8qwbrmI9%h+#mq;GB|M*hAl`wYkCqu@ zrH_(ZUO~kveN92dt5E2m2x`o$$XHFmC&&a{xywkRp(euRk%6I34pHCk^5R1)$M1YG zwqH}Su;!5-%TL^M3zk#8t5=uPe1CZ+-W>Dz^fYL>;8RA(*YCuLyA&cTq;O`>l}z6! zQ7c(7a%=8JIF)}uYsxRr#Poo*)jU$9$XfpMB+s>i{M^E|LN!}f;iB>m^2}CTnUv>V z;`!>qdTHa=we_-Q3ek=7c4n`QimtEj^CjJKUaT*_tFCWUj$Rbmyfbco!~tiVwO5Bq zXyfqJ-G2712!uAFt#(mFUX5BDHz;xAOrO0PXip?i9VD|@B>DQMV9mAHKpuHDN&!Z1 zPD+J7LV4>Ou4Ae8wg{ZmoA%3Rr8}BEI1@?{p?wvO0``My2xf8R>+YP7n$O- z@~k8v(YS20TG!`*Jy7k2mua9zV9k?*sgQ#!{}&OJN^@t#>g7F)l1C3$U2-yZ4a)*8 zls6nc#3gUtRBpdLOz|Z4XuF-N=c>j(YmR&;Kmnit1b~kIyL5(K4|f)G{eJ~I02uiM zvHNA-+h^D?b}`{fOl!L4zk!ZfzNV||8^q~#^Av5fyEzJDv+rM6`~^BaziQ4l!kuCH zP3=MHZ#tv$ete^|HH=JWSOfl~Gu|xHOT8(ly>c%*{m-W}nB4sN2V&JL^ADvuFu(2u z+!tcWKBWtBXTPrSd&KUz*ty+6eOVxC3l=OU=*uZO5)3tkf5*ZuJ+^c$SW2=9SY1lC zj}%@`aZd7Zy^|#VDA(UBm-N!LwSUyynCE&)?Q-FN)ZEon_PH!X)dS$J{K}nZ$8WBF{Z5Cy3&A44;Jdyb6@=4V6+966+_}iE5P9qt_?)bIecLFCrj}JbR z2{rW}v9RPjft^DDjA9oE@r;BruHZoD0C=bpK}KMunfk0Ro?dnvpCYVEC51hYbQOeh z%R0DP64aRNA|rTi&NJJqT^VQl0$~-S400> zM#F5z$%q(6cPIRJ84dfMvs#}1zs*V(cv057gRBAbmZ@%U@0#fmOH`|`zWxm^e>QXr zNz*z%28s@a+A{L2)Z!@KP_sQ1AII?5tkl7N>;IqN68Iw``(n)I%0fJrLRdgeiPmFL z_Z<0@EWurPbukeneJk-1+61xmSnYE3Qj#fC_fqmX55#f`UG!zb6X(Be==Gx$YWEyj z+@Jk!=uvCT<0P2GT4uJm>ZG%3j39Yd`bR?-e)`yuToivb^x|qVxQJz)u$?Nd98=0H z2F-dFY1GRsE|oS@^nNUBB{%diW=>_HiXPB}*UQ1-$n}>)=3bjs@BejHx?0;8=OuA- zG=nPp(h}eM()#U=BkO!78r`x+x?C^!7vBrpKO3Gf7fMEfU+zn2z!ui8w7{i{|08pn zGd@`=jW$qB#*bJ1@@^LG0+-Z01NwvDy!`Q@$8Vcnb1WgNy znkl#Wrx)MCcTPTdOhRWiZKkf>6ZqyD?w0heRffB%XhBZ(E@APyiB8?e6t~jv%jto? zz~#Qg;f06R!QWT%Z`=-Ez-0QkuWICOjjX|ko0g}I#`=>tgZ5OaW+;I7FtaS*xzppy z-&R)cM4oz^yr*@#z47iyn$MQ3TFTMC{R!YA1^ZQEPuOp}zVMU19Kz4}0n_=mxFRmYfsbn^i0#}K@xO6| z)c0IazPsBncsG$morBTR>G?Yot@cuXWlq<0_g@X(OPi(6Q|QzgxSqO~zM`L}G~PWBP`~$dJ0|b! zC!M#!lY1H8`tno`x_#c>J=`P5r{rT{x`TN3eG=RtUyY?_Fj8qh6B(PYA*4I>z-m8> zwLf1|zGvuH<}{k7KwC>mlObkSOklQI*?JI9AA*Ajv9>6i42A-3;-HxAY{S5wk&MIr zJSUNCi|Bq(Me?{Ow zw^jdRMyk(`o~xc509TT5!zlQ((Bo6Tw^b=9JnjE>TlJsxF549;_;?}q<7UufoQe{& zK)eRC$KpTe9T|a*|ApSw%@eHth2AMLB)k3%0^hs+b@d<<;lZ;r+2?c zDt6Y?`*Him`5C`Qs*e{u3x6ZIU|=U^?=2Q4zCWnJ;2uVY}>Y-rb!yxwi+~UY};mI+qRuF{_j3G=e*}U`Tksc=iYlS z&6=4tYt|rCURDeq1{($l2nb$6Tv!nZ2y6`q2y`3@0`LWA1tI|m2xh}nNJw5nNQhA0 z&f3`2!UzaRJTyKLQaM%w-Dmx+fDMY2kdW-=qd2)w9Fisw_#q)B5j1HW5~^QVVF+C0 zhPpg5u%j@f@CRHxU@YaIewOkU`glkR@^f8&@NqA`WFsT#t32-ZR%^~yXDz#Y=esKe zK$7%o#1j5RK)K;ec?Pd*-;x&CAWReDMPCB>Y$40;47(gcH4t--#ELTk^WzDf1PwEhp?W|XG=KPnwCM{io4!>A zEt~p<*>Q@T_*+CJGLQ>nB9#n;kA?pNmPYFrQUs0<1~?3Qq@wH?28ij=!(y0&uzR@$ zCejy3-NqpuU_dZ<2F6<%pFh5ld1VvxCZ?2b6t#Z@KPQQim};bY8yCCoAmfqYP3r+u zsFue@3M$1OxIUI0g;K0!w-p#*4*wPV8cQ7qDJ;t**B6;$%sWpmUd3i9aKTI-`ph22 z{v$QS6n(&>r$8eaIe#FY)Tk!^2UXf2m0X1KLLf*au~$7kv0HMDumWtpMglfI^LCHw zLf#d<$O}kS$Gx;JpUC-PHQz1O7R8-03<=1GHhYsJj1Ubb1|x>BrC%Tf>sT4$fgPmN zTHmE&(2FlHav!Kps!MHp*Q9rB#!lc>xO+ZW=^rqI5^TWweMbZh0O8tzv+#w50y>fI z3L6PEUcicpS5&B`kL?n)O7-bNzAoLTKe0H8j%Bm;E{OcuayIA=U9@dt@eB{NphYOU z^^Im13y7u#NWO{FkVyvI=LneJoLY`{G`Tv9yq$ia!;di&R3;M^&KIjv@W>v-suQ+| z9Vx&E3JEp=2p`GMfe?IH06`rRh97wW#>tPd5)l_DxKqIfm)Q^920KBRV7DxR1DYFf(XKB^2*;ftFSL`OYc>ekL3H8BdxkeG*a_C)61(Fs zkQ;${A@%&vyLm3CT)@EPN#i&r35)_iXD<{YDdSQ?lZliGpk+(S#}{%cGZo`G;>`ME z3wnwgPBJrtS_V1^L1vOna^EoCVBdUnz2Z|o9(x3b}tqOQS-2%o5%?Z^Bu^xXp?DXfIftU?@>c=sZ z{Ycm@TiYLvAS+fY$SWEvG}?S;sObUoJ!eBb!|I|99Z3CQH3`@qx$ zp?u}kB^CtP#lL=BA-q8q4W$%T%I0G5OA^!;qb8d}rb8h>UjHsm3K|1zN+cmXL>3 z#XH2yPzI}%7s3^8Da$B(syq}w6hkVHm)uRtGYes-^yQQl;N|M%-cDCJC~*d-3({p1 z7Dk2*Lw`gJVs;B=fik2R$-e>){JxtirJ>MS3Xzn?`uwVdUwdQK)3 z(e*i(K@tlCv*&T?5%|&W<>e9YF$W?a;yZ*Z#4SV=d}xq#4`~lxkY!MhsGI0@gxxo@ z2z*S`Z$J73w{`kX`v_#DWwK>hlbDm?lFX9qq-$i%We9#KjBt-8{7^|=W#~_48dVy- zPg<|_p~0mIK;K1Yqc)Htm-@boKRm{f#6fHS)1GqQLF0Rwx~hh1ood_M(OlP@>s-WK z#Xk0EL`sXL)zgYKYb2{FE2Jg28Nf09&16_;0cB{@@b8VxClcU|J z1JBuGa7w&Z@v=dKuWSA_*fqk~u|1p3e)GBW`K7)Ksmt@hW-mc6crR)X z4G{VuejxSW4z>>L+_LZ{>=PU|jvIl5cJeOr8WYp{;rgaF*B&lj8_%cL1T^%zgt`-$ zAcE{f%R>0U$w9QRFOEb`1_#|&f#W?EJ$BtoJ*JydJ&3{HAs3MgD3|n8)Qb3v=y|wk z1S+Vv3`^wA?9JrHRwGAA=2empnjxBXmw7(dswESg<CIUk0CNeeo3Zw4?}K{dp1#SBQ4isSB8xwg1e{L+2Kdlv9*$yC1}9^@%v zHzi!;adp4SoKR9H#zmc{Q?c5AD4ki~|2WN%#&uhPCCrrc?PUNn0n|^}B~<94C6G4`(hN_J?Cs zWC&zWPRP!K&bM(8iL9rrOKpz#>OU$^teTu%Jp-M3`@*8!f4N(o-Y#i1$eHL(w$t&2 zd7Z4CcXVV;rhV3O{2E(!B%C3 zV}rqQPyU?3DF>czX6|Ip`%rRQF-!AEvs*ALYt13V9qVoUY;m{vT~*?EqG`w8(^6^~ zKRt@Wd%wFI77xRpR=0h}`Ot%_tMk|~xBc+-T+=dd1KsN8N|Tdw*Xo%2_xt9StroZDhgHz~>9Y>k=v)08 zgENwmobLXaeloe*4CPm=S9i}SrSU`EjJ0FAo($fT?b|lDkM68<8E>gqtvi8h;mN=) zz6k;yT`6=JZ71Gjt}L9&7l`rR%B`&KFT(`7-r59_SZCfOo=mS!(_Ev@v*&ng4G{s6 z+z-*O1~-PAuXKm*xM&Uo;Anwa70BK3Xysl@avmmOIC6rBHqpFtClvh)aH2qj6qyf8^C zkcD-{gm)^6q9!lFLa}JJ zA0X>K3kOeXT6SIQU(EL2l%l-#x`e?IHqMXvfR%n=Lq7V8Bmz`kRU>r?V;LDBO29J| z5OA<55E$SI7;s|)Zh&SS69fba_(cKS!kHj{mx8Tjg8uysI{v$&fRd1e1mIW6(9X!n z%Kodh!x|xzB%rH#Q)P7rbs1?+Lu*S~gD=*fjc8pgZGJZa;&$N#JX#t#7!bNxT3Fe0 zy6_PHQG*lk{QEN7HrPI*%oBMWt5Q%eAP0AuhnFfy}p|Iy%oivB(1pG{TljqHT1EdebZc>j&yzZ(B% z;eR*$V@!>IkIBKt@}EQgN6B9;x#@mS{XbCgC!YWK3P3b33^(1sh{g+pa+FdH1jG*{ zAuOQm0(_DN?xQ@5(KkNAic{G+RTUSPI~5WK1@jSvn39mvK%xHgGdOBXB{Fm`AvI|n zJwLyxa5GN*;`91>xw8usR}ELo2{q38q|sVy(|P-o7v1^&7{`1MI=f!*EQnh}eQ;@=yDRK6N|!ID*L@MC8g`Mcu~AZ1?c zffkm=fQkG3^Odw6(GLV2{m%8v2Plw@fdBsihW_}#03rChGa>Z#_}GbS{Qws?`~96U zqJg03XX5&S|6f1|$#??AtHiw91J9ZZ+1yhkJPPDrq4PkHhZ(sy9IAoAVW z`4So$`aL(7bZ}tcVl!Et1!}>96&e8{AtVF}8V>FTJ51&j_YAMXwu3w~CfI=f96C&}rB=aQWCF!B9IHBX+AR*ACl1lYjP#Rf(6<|W@DksFeWMKz|j?bL=S|Bh)A*B!?l8PcLPS( z;4{%*w4>k!+c<=nEHfHT_^=u_Znc{LpnIGR2$XxbBi_8T_P!}##q_2CbxWDtwl}2Zb;+!qS3THdUC_K4= z@3NMft6oVqpR{Owic~k$+8sr9FeYbt+$i2vmz9V}m++#q1jgIewknWO4x|;$@UtQA zsj${2d_q1ZhFTB(Vaa@H!n*gHj?>wShBjO5vdBM)2YEQCUqUW5i_-Zl1Xjp%+lroo z*kFy!(DBL~dBSWZc|{k~JrcHa8mS+FpfK>fX<{;%Bopa}ww zZnjkIP&vc*gQ8z?id4b1wTnIzB%korU<15lQ>z(e{VSpT0Nd=m6dOFvHP;+Aq`)d!Z%lb(-u2I|qua5vKrs~|IkK*+C7MY#cuvnG(cHv^O+ zyI$ccn}tZa-JpKAKydi5Ezdwv%`-E~65`_WB=H}Q^ReDid~lG>sxN#R`N725 zfNfX)@bDn<^=ly*7}&vW((R9r2K2CTmmk(rVq&(wk`w!Yf`aBL7s(5_Uz>eZ)6`79 zqpqDZP?eS8GysBq z&MSEdZ3o^1c|C6qOY}p0-Ibl2J;6qk*&>UqWYo;k<;{=97Q?@``boqh z2i%p`V%=_!BsyMSS~YtY7z`cXUYhnZu8TbcJ_r)LwB4`8GD(>1gi%#QH#??5R{gA* zAkXk1wym;FW*Qs!tG{1&*V~}2-)zMp<#bq0nRQbF-G%Zohd-v^U zVHc`)AX|;Nk78W$C%J|&3*S6g+mqmlszXm$08TO(VW!1~6P_&}o5fs`r%v0!p%y)V#BR+)&}OC1JkesIQWrMB7z;}K z_L%L$Emzgz{BpbBs6XTAVbu$xRlf%al`bNN!;vAD9bGJ4mf#dx#h@lJ6``OsC11_s}s zyZcpQXY#E1P2p4{0^8RK{#BtP*z$X-goV$|;hdhwf}&X^y+J)S>)rDG1nd0gF?tqL^XxIVe>E9QaA3G}U^h=Y_!TWP#ySClIY$i;|mKyO?UT3_!rCry*WH zNGlhu-7gpQoHqI$)Y^^Wd){1fHjI5ge^OMGInb;T-KzsOiQUKJry-{CXSp}yqo18X z@lSjh*?6Ld&jQd&C9m3K^0S-Z5IykFARlN)+R6{e5**&kQmuWe^ z!{fIHm!6}dv{bo2cCBbWFl<%}iUx#s6*Z@$olgSfPs`RF@p8Pc!uODmH;1u&RFoif z8|lrD^$EHn0LP1ceD6}2VSK6~`NiaRgdza8rT*+X-P;>|7?T==rbFDwp zt%-+l1G%Lw7F$K;Dzqxnn7Ka;&RV$W;_=ui!z!-bblI!80%M%5cXW(o@ae&Z0(R6k zZP(rSX7CBdeA%?(g8pxK+oS_%%BcIJX$5M5CL?%UP6Gf+`+D+RtE09f_(lut+Csfzyk&!FLktxO=L^*gKOlH_c$N}WFR`A_e+)sIq% zv?|uzUQ-wWTGnkUw2C0y+Z?bzw_R0UyUic@;eRx#^VbE@ZHm?01 zF$%pzKwZxk_rdVa!s(jTDb_-VClL@{;-L~eiuatRD z9xhG(4xCUXEcGV$_!3G*mPi6%%X!3p6A0`a&o#FM(6ET8rH~N&@GfleD&N^dBJEh( z)_B}+PmWlMk2x>EPw8*FYoMkV5L#>fOd3A^XDbLtp^qRPt5PSFp$0BiY zeRn9)engDR`D~?V8TyaiNC@!jzft%9m{}%5x&$5(gRq#wo5B?D3iXRr+nIcCjdW7gkppf5DrdShA$` zKCI<%T{G;L2g)*w+s#)jCbw^dr8VyNXUS;@3@ZJGQTRNyOeolX+o1j6xOzwlW-_0$ z9Y6HqO}g&2if|kd5OonfF0debPO{{X5z_OE{Cp|X8~e=HgdOR84RROBbrvlV`*Cok z$v!QBEfsV^$|n7D2hlFHV#Z97R{!PHd=YKlJo>>^(4#joNlun*YCx8A?HALWj=ZhUUr2#JN{ z_)N&Yc|O1HHL8jm7CJZ*&tx>z@|ekb8qEH63B;NyMDHsJ!gImfeJ$sM)Ue~cj!9U{ zi!c{PA^rT}^mv$6AqGZFyQg)uFB?K!CO?_mo<4i#x?0u7a@++Q|EC=_ZQH=+MllZ8 zl}+NsR#(`=+PYY2r8P%bbm|Lrs-)VlliFw4jt$I~OEb2}%&>$WMV?Ox&8HcktV8bO zf<+b~@n^CNKlRA{aToy{jC`K3IH3n1{esn;gC5da91_mkUu4(c?A(%s(_GlAlaE2E z@Z~Qd2=2{Xyc*1Ahb{>tjV^KU(-kmU8^61Q2Sp-qerT*9i37RcrPXOZxHZ7yCf)W5 zNtAEgSBJM&yL|R>w*jb4js?SyTm^qA9#KGw=**UL%5yJmvH$^H)Rr=FFOalB$M?3? zD910~Y`YOLRb?pFKQy_X^wMkLiP$Nv-SC8XP=N=V?sa5JBg*sEeI0p4*LvZHp0wt* zZ|SMWj|KIaKE$H(EYWfPMWj_&@D0{!vKM2pP$B1r`SroHOYRD=o3YQq`dVIWbT#8X znipc+6Vzfooa%pFPIF%SL_=rxsiOU=xZT^}6_M{b^UcP)_FQRR^GgS zayZFs(r*j-0BV0Dw#nc;U)X{xOK08wkZ-k=nF48QfHJA2=kXe7s5R-`&wD;!k-__s zaI5uZN{Z8(W-f3W#9$)Psa?4O6l9zviaYz(|1d*FEt0$hHzhJLI;a2JTz`~dzR~BP zqgXoa#tB<#A@){){f|4Lfjap-7GT?$Tn0Bu1ZcV1v@Iu$4|aEnk4borYI{|$vub*S zTGs7(z2eM3<1%b3HvZpmSriFyLgw7BLdkDyh>|)lq*+hu4HP>BJa;p1vhLJz%7_8p zlUww(7_h^pN9|cLe2)tOowY~A1^xq}G5rwViq@+zYQ@RG`!)9p^ce;8arZ(3Y{7_R z7O*CGgRjfKp8CxZ#jJ<)u~OyOqeg2ee)eGKxhTK}{aVFHsOZ*0-EFHs_>!F}d5ddo z#tP!PMhX5PQ3p^hYXT|H>tL$ILQOG+fwQ%+;yd7*R`dtW@UN<|U{yBr;E5^4lR8W` z4>G*#i%DZUxb#(v)g}6(TJ$bN@i0UJ!n*w}nucUR$y!sSco}PGkI9|jXfyT3(oad7 zH?u=z1#XX6;y?&KlFOwjyDr@waDP=#WXueaBT=-W8>|bpzgV=-k$o5U6*j=1A zBcNyz*pu3ST-Wu`DAOHWA=0K&sZ!4mA~#T2vtTClf^QB{xewztte;g~e<_0j#UD|? zxA(4i(h5RolPPc7!~Rqz%;yXN5rM;&upGQf`=nd|A-$@Bbz5(8<7wz%Md%QuJz9GW z(AQPDpT7*bpAU^_Fzxz25p3wglvn;r`)$gB@d489+*sQ_E9-E){e`z{Gd@&A&8Y=t z+bM*|iF@$8@n{XVh3Vx_A9O*wLVn5WbTE@1#`3Dbi)K-urqJ8`$h6Tk{6=t$|6Z2p@g}EoUw_=gDN0(FWq`p%scP!P~#@>|*df!fT z3Atefys@as!k#5CErU;j(WQNJ?U{({&4eq&Sq^_9)vbL2oM1GQ7>vTE_J3V&-(G_V zXZnIdnpsn`uk(xhXKFS}o30iF&SUAK3yKHce*hyebm8r)5tz5qxee!GTKD+)ll&mj z1U6gRD`xv3W(v_EC7v>6;W2N$(h)}S>M;%s< zsTO*FN{X&Mcf4XbXf;10k2meHDC}2CUPcOG?rvWtf(wSO_sgxFZAthlEzCKq`nWC+ z89S*ur}J591}f12QANw_g-5VG9`UDa45I9|t1GHU55{?)lObGX1s5dT%c(nGNCKpj z@m)!-qTDIphpE)nkuh7D7s|k6Uo_Re)bFIbk4~zT_T>xZ?Wzbju_ZZzd4VX$jEqBr z(FftXHr3ergqyW@4#yf<<=RSh-(%8+hroj*lidi#JYUUh0-e6k=6ctCROBs;v+qr? z#!Lm69lr$z1s-WLx?~1XQQ_|)TZP7=U???G3;r4!Bm7D&Z(!|%R@Ef(wIv3EtttaXr$eTYl z>8cnoQwM5(tlJWd42}>&9Jpkn`a|X+;Bm4m5^eya&3m|L^w^BQ9^^#bEGz8<3Q+{i z3L*Gm>wXh(@!#AXk6>G)m^^}Khr9p0B6led&46IMl-W_}eF1@%Mu^$Ex#oGV^Fn{b zd6Y=o=?z#)fa@v{h;xzN!E4gG>z2T2AJ3UnaABe(pb?CY4ayoxN*~l3yO7va^T5=F zq-%rhv}}_X#1zHU0u`_3q7?X?LAaoa=__d81nY^^cAoVf#tr%7Hru*K z4SEC-{}y;5R+Z!4$d6`@0X&ye$N3}EfWEENpVm!iYtb2AL+(nr>E`9jVTQQKkQ2xw zHCSNmP?`4oBcsHv1|9mf>N%_Y{4=1QMRN9M#%d5mvwaue!uPpd)*hFxR9vVEe%4B0 z#3g#q7UowKnM%qljjH+m$j;`gWDxI*+C-v+gcP4u1V%Z5tfCTkQ?sHNl~Rkbv=zJk znkO>@1heVo%h|u2+qbH+#;%e-ZVVrHSWeqo>(k|EN<0nVjPa29Ch1z)8%p z%>L!4G|IW(|j^_RC)kZwPZ_gu_L`De#$ksx+bznb z>$p4qG~%Xm`H}=j9Y{{hcS@n&=-KeFy5?Nu9D-(Mfcl%Tz@?i!813QvJS+EoBTt6z zVLtYL*iDo{?%xlMCC8nfgT9H7XFDS=>@V%t?33=n0tErH3n%JtXxb;bPioks5mL&K zX=(c6#YDF>a+l25yNT+$p}(iz?ch-M^<~9(fU$%<$g2T)RemhZcT&&%Ee4OvdEii~ zz!6#Q2|E3WJ>B_ip@{gRPpS>}=uiOUlK9eNNygu5N)Wr=d!f=G^5SzXDukSrp$l-B z30>}CchwIZ(CZW3Awg|_fmnfGEYw0AwwoMq_&G?OWpo0zvRISwM&@&cpQvWMgAwui z)GQa{4_7*ByhMM(YPoD~_EHIZv;y7bJ#2%C^;`{it7LcgndMBQ#zY$^S_t@}D7SEkZ6_GtKP}O^uA*dBdI+5{oM) zh+m9TJ}?!Y3zqarB95BC`Q(e>}6R~?f-HqDuInW$G;?Zd!l>^g(x6-g$`(D-) z+!GjL=s0{Q`389}->TEshUbTk4^ozfHSf-dA3fghIZ~UJ@uz1>1Dd~(pH>!=b8e3_ z;^+N^7gL(K_OZd9^R;Oz+)Wht1>$2g(kFb+lSyiWvbmqvuBI>n8H2Ip!+?`_9X%*A z2wgm(!%iPuN-E}F{@9lMoK00&*a01hCLF}~ey!!#b0^o^S+zRxl}ZJnBK53n$#|ng z=GzP|e`9)sZm?bHYC%3R0rlRD!NGm2AAU)YA1C;2q!UA_x7G7pn|>tOs9y9o8Q*|k zw;1}`Cw1C}VN}p-!;ai`YLa!!_kY7OOxtC)EJw@}HAlkU76j`8q z)%D(X6x#`$hPgVFYTt8_3J*=}k@F(F>?ljDW#1jl3uws1j?cZy{f<)jvmticD|%Y* zjku^^PKrPN3WcLGSXkS>Xko@S-Q!EyPwQmLBHO3g&u5657B!=E;vhN6lIn)>`()Ct z2Cf%w=8M^RE8GX@w+ZeY_>R*T-6FUc^#J{p0^Bjy0vl&-uaFd)pD5+Uh)Mg+CBq&qPM23k)5CyJn&yh3d>^N9U5d|NwPTM@Fi^YeF_}s%XMzKW0(5bl{SnJgwa=&2(gpa9XGjOQStD~dU!WQ zB66SlMuzc7q>~;vJd@-2DH<*c0y%%8KI}%H;NLBkR2<$O(j|D84r>@6OR&qh^h`K5 zWnbWVS|?q`{H#S*rav`Pl?`zc(u8r5CrSf@dpMbzp2EvnZl*myz;OQYj)6u8XA7sLE>9?O)++EiI1Z-2SJaFag=EuHhH8X!cs+?YN0$7 zC^a>h2oSBV6{RsdL;_dIb>=sN#aAWiV*} ztn$#Mrl;@xG>%1!{Xj&!Y3v`~j=Zt8_1Uo;3|Cl9*EVl^^gElSD1QA;+>Qku@!E_% ztFFhHV%1JQ|^Pl#PYat*ytG-Aas&$W-2n106YAr*j-KMoBdlS5|P}vW~joxOEem=n%KvA9;+yheQjoVdtnt%4zp}AJKK^rV<0z-rgkhyytN~Mp7_!s?gUgRig!oDv;!rC$OF4eGOM z=2N+$+j$cSqgoE$A3%_QB9t^~A^7t)CbPlmBz}TfN7jTRLdyysSPXjQy=y5U+C>$e z2q;kIsEaCZ-!pp&HiYn-r)5C`?+#%|rh?lHYGUw?=Y$U0hC#L+PN132@piMb6L2Rw zb7-!O9iwV+RodGKp1T-aDP5m8Vu+z_+%lqyw9Z}sb=GozV-}1^P|k$E$P&f6u%2NA$6r%dEHlies~s`lbGggo3XMh}qL^=e+(n@+i^No_ zrEWXnhQ)`Yj-9EM9#vkHf3n2j60+cr-o}^Jxqx zU<{t|wA@NG%j$H_727DKe#1nD2f zrG2_Hh+6zQa_aa}mW^#RyCl(>5?gold7{Kb?zWFlX_Rd^l_NHH`^9{|Jf~rB#QA>l zVQLH|1F6CqMt-Zx6A%WWOg`3vqVZ9&pVqyZqH!)*05h^E1#e9_D`&fGKFyqG{|fJz zw@5IAwte9Rieg`pEs^XGzwbpl5Lk6P+YOpa$7Z{;{I0P-ITpUnrF@Y4;ZEYmVStZ> zh9oE|I-J&tuARssU$4XKkq8E{3u&^F8{b3b>! zNAb-euBxNcC32Io^r3&oDn&(%gxc-$M7pt zPp!#dHq~m~TcPMsX_D*nCDMrCpF+*F}Qt=Uv`MB|5Kp^&cA>l5W?T&mEa`o~^dD=pc-z zjmx^J?v1AHBdrm3Z#SLY&g;%cN)L?Q4Uj~!(`4xU;E#{O2eod$8lrOZ*4MhQ<7hB# zN>x4R#Yh49;Tx{vwea19HwKjnLsHZ(ET_5B2}3eaEJ%Drnv&0O+i-CAm>$a_?d!z*Z}Sx^uM?n8&7g4OVr0A4pLf4;8kE9)UtheuB%{<$hfNqj_xwguz@*?Ihyr>bq=+H4bCuH4|46 zsAr=Blf67(q{lASm~gcjU^y+HGCG~8?;>wh(5)OOV=BO=!8oXvx171rRBrwZ0Qf|@ zaVYmNLop%Yn>9F{4hnm0?@m^8y1!IR;1Plv6rvykTo|RIENOXSf-J>!vNqqg9w&ql zi&`DlikgtfZMVC7jV{Xk@z}U(Xq_{VNG8|B@IBQdKx*ZqqCXFH>3B498d!I{MB;)A zQ~NjbzIh~gJP-C?MV4W%}n4v=pTyj2Q`rP<}nOX zI_xj=}BZ~mK!qZgQ8>W{YN}c>RQBZE#0T7i|(E~0c~dE$BMK1{y5u) z)Q8dAX`G<);SYD`2PB3aDHBR^r?<6lm!A>67a8u93w}hg@+7y0lmo>?qZ8`26k>%UU5fiWUpS^p4>}4 z2p*_2gmpFG=yLddC7Kj_3ukSvBR`;R{q2`diwKDX$qJYE(+gW050~@lOuBWV&YTdw z`wfHh>=7*6ng^UJw9vjx{Jm1_)uMq%<aUfVi^}Jz~0_R=rh12Ks9*WcSyl+@gZ)zx>eh;!ymA zJvegKu>v_Y2B$WS%W&NKO-!X!bjF_i!^2nFL4WN2nfg#J&97SXXA(&KKT-p*%z2v9 zg7kwr>=~T95g&^^F$ql2=JsecH*I4t!`^xW?sqcjXFX>0S8qXD% z$78`B7P;-E8+j3`F=Yp<-8f1N(-U`Pq43AnCVDCDPyJ-bYYCi3nkmf#O`)X+U%T-N z&#{(nNU>MZ@u~@JNUpNAkGAxJK}4PRTZ8+kQUCH3g_1Mow%(H%%I{88-;49!KfH`qU?>Z;FT$aJUNR(NomQ{Qa79JixYkmGb zYU5(FhZ77ElJUcrX91a@kPuAU()HWXj=HnVO#Q=&c7JTe`)G6sqBR58kN|Ohydp4h zmxFjZy|rfb1XJ8XwpC{F*ZYy_t0KGIK7RNRPbcSChFc*|R)!4?N@XUm7t26^>Dbu~ ztYu)J+A2`?q{H&fYT6a2({^V-VZT(k#T<73&)CWxKH!yg8@aqc-~Ns@^Fs1@wKd(B zs$1JC;mgtkARS`z73V6m ztM@}t!}`FBVvbt`?yNPbLHpg(G1vnL;b*dq4bCvWlB{iCN1;@ElL2ra4{)S8 zuSZi~C$+$XfV=JSS;7FFe7iY#Tod(D)<}8t>At`)?ME(rLaDsZ5cxUwsbT;rJe#$exJKx;nj!$OQbjn12o&<=B z?WX=?OgV)rSSE&aDm%lnz62$RH{Hz&#Z0vT(5qR_EI!SEQ-vqz8a|r3BepwPa$vN76@Xg80WLf4LJ((m_A~ zNg^DcU5c8n%Eg@^#eN7fiHc%7{qB!?bwm^Rt`W>3er22qZ=cr3rIID3(P!fq;if%G z2glrE*g-rD8qHim)q8emmkT$444t~8+1lWh2vCTx2C$angOn;mAD|IGU0TkS{?ObI z;zGn_C!8+u{;05<^Tnr_uB8&EQSVMZ-4*y1`0CX8+L3WmBpk!^JELF(L~KlQcEj65 zmcJ(4N%>D^sO0p3hJ2Z12?qz6v>CghIA=%VQy5At!{{ZGivyul&euxaZ<5PGWZFFQ zjmN3%mw%jF{}-PZ;JyHfoiGoPpTR*kDE6khSybSN21O(jv04HlTS)4wFlwhmBb-uy zx+EcjrWvLn)=;>abYH9>^nD&)*VNSXkkX<$PPt4`CAq{GmG;!3Xl&W|a~oIDxzP+% z<|oQU{L*HW$zs)#qpy%#y;^l-Tg4uF@n#0M0f^6}{ zmG^t30Uarw5>N4hYpwpwW~BXu3Dddp*&h z8=SkI!K8D2ONEaz-J&j`1|Kq0;??jzPekGIn1xAX4n-KEz!;Oa8UXL~9#7^3;}g5j z7RPg9;0fvyZ5godhFn1TW(G~V(to+9jn}KNUdKtTpDx8*aW71DhsR-6B&m2U8n#k? zqR?<{$!g0oTdz|ysMZJeWnyHUqAunmQe`b%=eNSOF^pUbm4&^6eJ7Na-q!=3Wwibj zDf|`n8}9B1bs33Kn8Ys{5*iI?>!*0kd$sfcHYlY{gH5-au}6%Vd17=y-tXIR{&=~0 z1&0PH3@0?NNZqDCsY?kqBQCBOiLwWt_L8}g{jh=@A|oT`(!YvD;l(rW9tK_vOU6@4 zz zt7^KY!mH(E5(`gb3bUE}2|=mI%yfdW%!HH-NSLrdMU*>gdN;`C=!Qf9Ff(D0-5-fJ z-*i&x!NHOVcD0Wss?St>MbYnU&XW{GzoZF2=cAyFNWekeA8(Ts=OF4nLd1=s8?pf5 ztn0ae|BX=#5G2{qw5L7ad;ZTs;x_G2JMM)=1@MLU}U_&~pRaZL)VdCnS zqq@e0t@+1@Vj$>LF%YMcf<(GyYO%?;x8eD_p+9E8tDf-*&&CfvObIetK4LBhA zZpmb3)~0UBFq~^8;=BUlp*=F{i#NC$!@_&;~VU~4?-o5&>sGS5z z4iK##jzO0oU}nPtP<;6>Kl-#DU~)5(2<07Sqfr36v}o8L@7LO;EJ>_LaVTt^KL}>J z-&_5T@I0LF=)h+t)}u5c`?EV&Vw-~rB8_&BCVZVGPI0+(%5A~FU`PX`OKctnB~oGj z`X&lOD*p`4Z`0rT?ZXC)v7V5Pe*ung!%Ayo{)H<2Q?R)L7^~F^1t6eQ>cMVzHBwzt z{Lfzg`@eiDl)MX-tb6RT;9ms&pBi7G7N)1CVPy;#kp8eE{c+;{FNsV+3wCg+s(nY= z;Qxo_2QdBo*Oe)cTxo9+C|Jk0fcZ{0zMlnsy0sVxkt-%e;JpboLQQO!*_ROVycs z!sS1sV*l>@-(v!BqR+4N21t&PP*jY`EhsqVi?q``xI@Jeevc^verIBJWprd@WB@W3 zyu zE4=K_T!nw=6&sAO`A}Yo8AI2aKFk^$^o{7B*4poezTC(@?Z&mN-i6kErPIdve;wBU zk#?;lK%4O17j8-*+W8GB&f(P`-r<7Z!rKY#+b`qbN746S1PngF2g11%x0&|<8OQP- ziK2aoI@y(k{QmM&{hKoXqE&Ph7)9$YiL~lJ=HvWPEyxG&GXF0-+dpxf?=u{(-s)uoEvYFX8t@0brQuaj=ua!@~#dKT|>8L3rjxpW(k2!v8#Y@OPe_pP&1i zX^_2(+Bgf4yP27p7#Sa}_qdiHgqKiXZSViD8{FC2FX!G?JnszU69u&Z(C<8ySxVkx zUqas)sm}C-1Xa6+bgjQL=>C^Ryu|tqix#}a$+LeZ)cu1rY3+u0#-%|Ub+Us8`(Enl%pcDl*pfoKv zP5y7$pI_QPc6On8$RyqVbGjD*CE_4=-@biAY1({$LMRtxz%bVBxx>xxAzzXjxTPAU z<#NU5J*519guMk+RqOga3`^=p5+riHXk zUP*7Cqk^3E4z zI)9uS;7y))hvZ?8UFV+$O}tGFuuZq2viqZDi5+(J>A_B@85sP$;p9v>0m9? z$w21qJ@g?4U;)Fz!bX*sm!p<Xe8gie@Df^p#rr$Q+5CEUw07L-IMqcbT3N^D#YY9 zLWbqiShg4$50AEF0{zj%RkDa0 zfQQDmVGOuiD(q@g+pXaL^mUZ!kcTb!n_&13ExKZ7i)@R9>#|@w0cE_hVcf!&1pj0G zp?`qj8#z-=WKXyFpLhJr;p?HC_MZOj)cO#840bBt5`VFpq7=CjMlPj>1Jw`HpEF{HVWO~rueUc41ELc71)4r zgx5D6({@XE+Jz?9_lf_0SKT3_=&o<`dko$Rr;R5JE}y&l-Sxo1k=Um5e|(AqKvxN*IYPE?*8_B^CI|O_cljR zJKwBPl3ng5O_QFE+$AJrqf|?@BsO{?;EfK>qEU$L2lucGbh8@(>R5^Vx0ndxUjhzF zPsq^lQ|K%@8D5S&sx&&l7D^9&_KGAQ7}p7jE^b*F=MgH5`+?;}ZPk53xrG_04gsPG zM0}ka5c9nC8Jc+SM>{J&488K;skWTp0^Vv^tI%Ef)}7O{sf0W4&9;<5Q@-I-%ftKx z;(9idzxz5eQPrux9~miW-;&c3o!$2Ece~v^;EtOB+!|yh2CG-;98zsQrx=J#V419H z$j^VX`oaY`0(STb9O|tJ(KI~(xRmBkqw>L)=*yvpM7_V*c7Z68)svl_ow3)M<>rWQ zYu|3oIa1QgIHpNQtz47$7y20+T4^`L!k0^Ux5{X$NX3V)d45SZrV)MMCZ4 z2H)(fgrjgNE7)ZS`P>EYEsYzA0Av=5zg$NQ{B*9&>%Em$uWtj+KeO2#+$Kb=W}Hg5 zmR%3%1a;XBvc*2dM`erdmz{dLJ~NrVpqK-k*GiK$VjnDhOT^xtx{8g|x3vIIvoEdl zBsLK7Rp^jqOd7XSf0N6F;hG#gR5;;#;ASQDIx1PT$YT|h>I$aD3h%zZ`PGVTMtXbc zA5L~;({aj>zTwnC>dEQfzt>GCZl9%MMHI zS~%g**FxyDLVGwhmFG45k)huFg}VQj*=R!qzvNXm%$3=Jx4m%l8TNe%@u;Xmxfk*_ z&fL{2nszaXvPgxaAOm$4RoTQ;TQV?J{(b759_l2|m5POT{-Pyu>ygCgo4X3sasY;n zCMPcXPUNi2qr_juqJUcks)0!x2S`L(K(o~@N-dL0xYXYf;r*5F`YVgL*)1+reo8kt zIvz8}DCSwF(L#`ReZyh-&=*A_mLuo~jf#%GNtuxna`k$e8{=fEDj{a~!$SqY17!xz zFb|4}c_YRm02Y=e>9;IKyc7`#?AO1Fg;)kykhRjdt$GIP$sz1)ix{*6mKLSXEJ;qWv1q{PUoopVA0h>p_Ol}U$>6tNvg<{Z1 z4uok+mgzQM%DOx?UvB|C@d^A_elL-SD@s%07WobxHEJ!>+?lV?mf8mkny#*=iqr-G zJ{T0ycYw9SC?ZXRE89p0#xSo@^6vFU7IW)zfOtWtj+pHbPop->C5226HbU%7Z1G@RH05%?vH)oVk7a^gmlYx9 zrLnI(^^8}Ip^)X;_Ax%|nHP~#3GG#e(BveGG6sfX_EPB#weUr9M z^M!f|H2@GNoV3^>X{{FOEEe9h6XKM&wF#sd4>)+kJZyM9W$OA5rSl}!rvc{{IlWuF z#UebiY6Y4ij^-nm@44+yKQGw+bvhYtXjYS$htMp=I%bF@_A@}%7+_i?1^|Kwa5Hb2WxC4iXXH#=$N9jlj+w>ig0^sEla>> zuby0MLO-Kz7lBmr@c8rO>SUY18Bud&Ofl$+*%T0S)_zSfm-WA5xTElyU9pqtQ>|vJja=41#PXtb86#X@( z3q3Yooi?@Zx3))|4R*bK|8S0};lq2*mWghr)di2KZNt-p^K{R z=Vakv!!Z=(JZrQ2SbDAs`+D+v&jc(wFiy`KD+qn>vZlLG%pS&%J(oD=n=x`+TZ0|$ z2-S?UBEg;;5$v1i=?NRg7V4PzB9sSaj*U8i!V0NDZ$unWO9${DjQSQQ>ttb2MrDmZ zxHxUr{j+gx9e}{V!&mlaR|uNXacX_!N->)Hk8k?5dzGMs#kp$J3HwN#3WFZ*W5T@$V z(Chvz2mz0I^6Ua&qm=}(J2_ZAIW^8$_}_#ifc2E8Wyt=VbRRnByoxC}A7mNf*#7jI zeV8e20y2`5q)E_Q6OgCCM4`;%YPxN9Jers7CPYVSa2e%{h=$9;@MC+&UzdDvyMD+0 zRyBNcpQ@UfgF{{P6Hdtp8E%oP*69X>*fKXvv{VA!)XY;9i73~wSg^%O(?jG0(}(>; zPJ`Ul5$xlb70>IE+d?SA!%lv+hrLAOG#DVu2*P(2x5RbMPJfs)&VW5m=#kIhUPH|8 z*8Fq=&W<#CZL?SgP{55bo!lRR5K*I)?3uZ5)E^rKF#7u^K(1&6GkC)~$VI;X6vCM1Ve{Q;48fp^WRpPZ%Ig>W+Vr)MaRQ;PagTpW za|mu{NYs2Z0_lz8Ol$UE_O991#3%}&gD~1Fn!d6WaXZD(2|5^qvC|MkbL*6!fC4n zzTVsmjZS5Ytf%Ch?l=iq}ElNSAnO}*F{;#PV3b#4;{_R_>uxvq02-)&cnWjCu5e!5TJ zi@;M3x%E`4gQ1=768h<7>s(sfXhHW=!wj#-`U$xg&<6?OuE(4r5@8ke(8{I_g)WRf`~fLOmw&sjg6yYtHH;qFQU zI#NI*_@KUq!0Sfg?(%p;!Cu${G-!il5Ic8Y-sN8m;qG|Vv0w!dnC8y?*ezB7IS2YJ zBeCCdA)iAS0s9Gjs{lb~;aTYAj_gj9FQQZ>Abd)Lwy0eq2Z-`^jI6%6p0$y&FPXsc zj)3@;t3$aQ)HJwS19k#gbdAd;wL3~^ZnE0u)8H9yeLa6r#yP!Z331To$0y`T=-+PF zHKhfPZ*jWJ>3E-rOrM(|H=#2k zXStkG3;uk?Zh5HAKJDibhD7}qV`5DVCYs_CVQ)t&?kUHj3U?)+=vo94Ak-Uo!918bZsg#8qvTTi?Ibfn>T z(LS5XX$P6kezP=7Q`WS;+3sg|b3VY3fJvvddjW&@#JMs!HiPYqtM9L<&G6pfy%CVo z-KDZQw8sp+`VeM)b%;Qe2htArxVT~;Z*sA_m^uqNSdfGFv~_=;CpeufwdHyW| zq|$n~Pb204x}@M?YCl_&epVuJWtG2Z9#QeA7K^@3pP&#z?O&v~{(e;qYV93P}PGo4k zue>efU#PVz<8AGK8;|18x`EAn&7d8$CbQcRUIP)_RZ7f;k4yNT7y+jM#(Ic7P{BTh z)%1Of#OS&KvRZ0WF1EMWVyK!iKY7%y6ggwLDxzMx_0uM#dBIF8XFtqqP=fC zX|Yod`FxJg>&nq$0MS9u16A^cII_M}r(xfXqhmS>A0lfD&>5M&;!d6=&hz8-T)TO>0F?s7gdSge}z0SeTa$H)n1s&(p2A<9>t zJJw0(%jmU!RPVYJHnaTnpWgc`S|Rk(3B~BVeAO042cvZJ!p&b~Lo`@@7@n?%i3Yo#g)~u0C2K*7dt~U%UXC`j@H!1E#7*7PMB;37!g`8uRoRD zqM-OUI4jWL>6e}Lh!h+d{1 z)Ig?(*2QD{y$f|qWNKn<`_?YZq8Z}y`%z0@2w3`*{*Mc~hAb2l1g5TfgsziZ;g=O6}J zh6Ah&t10!3_%>-Xc?n`e0?D1X#k76uA3NwJAHjlPbke3ntto9|0%%5YoCa_uc zDmr%M&E=b;sI%i(r>g3a*r{cUpFiMj8MCt8mmFsHGC;oDu7hojFCbpM^+)Er7l5ZY zMNn@tsVI>3A|{yOyry*OqleVY`8qs{t2t z`!*lI#()$i0{VYD=7j{_IT9))lX|HPEhwoBY)( zYJVJapO_I1>NvABVV=BlND%gBbr8}Hs3qY!J;lP_e0X>v^vX6#-y2sBx*bhu2BW!j zbXh08e{0MA7Lap-!uRNDn^m#zv^tf=<$GU8mCL{4e1VvEGR;t@x0Z}FBIC^0;Wrs< zIc+;91DMkV)2A=8!KbRg`+joUc9=0<{y5m=d&lFvnmGY;lCB%t!b^%rXS$_RJTNAnB%(pmrLB z?c*W-?M{|80ERsSK_GN^O5U=9_`nk?4{FA|uNHt_W|a?OjlkxFn1T__^lK-9KN6PB z2GSC#st-ekUBM{xRYF)dCj`Ob;?WWE-2Rt>$`^*a&K=SeFj<@>nlLa| zuNgo|^U~R_#(Zv^OA1ie*|}UwVGt!0)3A>(n#nKa{j};~_~0iNrZUK!E>n8o-L{%c zFjy0wcjD|LU+T`;I|2a0?WMJvO&Kq81=%@0Y4SrnAAWL{z^&z~b~+$@-->9zfb-Sy zd|j%fn({bjf1rkkYI@b+mlt0PeX@zA=RS7k3x;2V%y*fIDJz3l7pR|!Q1q_R=mIn7 z)OQ1h3e47E6_SD8Us{OcYLhN#bjZe_mos2Yb0#L?0?MK``M~qrW8lva2RyA>^P0y) zR*#3P8Sa2cu7}+%EE+Xh-&4gH_Q?TixEytWlbUA$nVn2LlkLFx8Nw7s?N_{sd@+oG zf#S%@51|SLlb6fh2R|s0zsW`vNs6CSok{tf<|YUGU@%q};J<6XozR1|VuL`E3Pjyg zRJ4eG$57OS;hV;VRX^7^oW>1ryOC)JEr;N2C#YS&M}CdBrC*CUF-0N`+d&jxpi~>F zZTp?fQVsr8T=nq*d1at-YhbhIHH(=vj$5sl>KS~tO+%F^;_I#bd2m7Gb%>5;Cg1m=a8B&d zR`S@8={nS6*-Y9yUq*!MUUfMZVbP1ye%n43(&hTNJaPz$TSez`>woheL$E?m`dDnz z7?CC5<|z5ozy#Ntz6m0-KX#ykQdE`Q`9MsXKSua!NRvU2-}Q+@(rvfA6R^g4N{D-J zYs_ZG8LX_I1a4U0a;Tl5_8wh~@Mak3ltS^OF-$M|s`oT}(m81Jz|?iiMy&L+ibpzD|Bcj%S>kkgZSfCWXxT# zFxH5`UvqybMv{2N?x`KGE#BaL_tM1;6nP7%eV8a9qX(|pe)U>ngboUdupH!f;E~CWo$9h5I@Yn-o?h)9| z;P{jL_cy}vqlQJJ#?vV?a%wj4E~r&3YM)p>0&O}5wzg`Q8XdXm;*~tP^E#mJp$c9V z`w1sb3A1_uM*&~mYL8v3-xsM{^#84l@=uY82jx^uY;b>VwcwrLpjZ$L5no?TOd+&J zU9pYG?ZFI(gl|F$BHied;TsV-p@=F847XxsB+1YAal#Zt-E#36)HDuqb6+J);{a0F zqvnUSc&{jR96UCEAL2Yj(Hy`GqZ5zavOKwog5pChb{A^P7S=&qjFqb;)FZELyW2h& z<1B6GXa-XP-J@2+-M?eFM$PG^<&MFgS{MX&)SP`QrIj${}-{o?!y^@$HMSNYKi z*LB)E%=@zy**`W}T2tgU#sb|xU|WotJ^fsHdOj7X5etQN={}kMT!PVBIvSSfn6y=5 z^w}6To&X;s*Hh1nm;t3J#ag=U00y|1DDTluB0Mcq4ZFS=snoTp;9 zhbkj?lO}w3tqnGXH&Bw5TrS++?p66ozDqw-WoyOSr0D3iu}txiXAGr2A8OwieDw<6 zXE@;t$8<=P`4D9aM;W27Mwx{(jzM$oRu?QumL_8QfU5AgCj!#xF3BgZW4Ef-#1dXB znMhV#Ic#r2e#Es>Ii53rOf#8gu}`>D#1@{L@HBV|nm>x3!pX=1+3KWHhNy)^E8Wa?5u1q;S$u+1M{xNvP(wtI&yAaPT)i&7+s}D<;~al{e_hozrymZ3#>57ye!Lj^MBEuC=`)|R`+MsbGoZHz z%q_`*!Joz`m|Uq4IG3zF1l4nxw1cEQ#aI|x`E4TFc6*<>$wh(Jc+i1K+&hN?@IV&& z!*$Yt7T{qS)Rop>h<0D${zPS94S#4Lz7PRxF#v<}58JMtD$~=`JBcu6?zm_Nv*1+g zq)eOE4hljdh|b;Tmvg)s)$f&C_5bNxhG2?SyzOeK?nQ$zQ8$eIidFony$Zw6{aqVM zjvHg(##XJK7n)pld$R5Q?+WLjJ~!H2LV4j*1P-1W{&?jRCRRHZB5ch}vZ4GMv8)od3`&iTDBX4`T7Py8bl zcH2$yJZtRWI2Plf$?P;@u?hcP3K*dvpb+Lm>nE7`NF*Gzi$bPWqPf+FWjqaU#cuwB z=SEwEJr+3Q4Vx@b5_9XpZU@YD11S4}WPT<_844|mjbJw%W( z_34ym^V%VWC`4*Is`41ezc2-3!+iY_u(ntpYPp*#OxXa4fYne?y>*45N-wr-t~z>) zvvIXo(8RE#)tS52U3Ho8_4y>Smzro$F!Q2jP16+Pkx7USQo^8gE&oGHqg-(LG^1!! z&S`8f^3UNMH0zRq{qF~NDS0Q@P_NdF9HHC42U}9U6zsnek#Za`zD_x)xZ0sM9^pTz zbS5%rLSP~=MJ-Mro36ZNvf$-cAUJ-#>^j%zIzY5pMTnTyaUIbjhe@wVsiy1lb)t~n ze0KFRP46GO{rsT~4ZN`yxh-m` zsF-K&JqfF3q!g3WlHr9P9>Ag1T$R%&B$^>h z92j%TpqDm9S$;2x5Id9|mtO$FXI1rv-%;o4OtXtu%5_yA>Lml0_y&pW+uaZjupMS7 zo8f{TQr=iaPkmH9s60iz{n2P;)H8?^N&c3`#(H|E%J2d+F6aKFbraAYoLVNOJiTJq zJGhfReq}9c$y{u3bGjDE#R$%1ex1H#DI#zm`B1gFL*_aMRL=F|zX1n?-{VWPF5f)V zt%{kt>l<9eY+Q^FC-ej4eB6cbu`3hO)ex{qrF$H|{0|)@Lp<(|d-L6QD&%}DV^o)O zdtz5mL;=V9Hgr38OX0-k+rsFTp`wYOtoJw3ukoczZ^u`!Oc7h(T&<&#AU7hh>N|bc zFvXB79&lxe07)O@r+^Mi{FK!LIIZ?sjBFP~5j6ci<}!r~eMBHx-hLC?C<4DTdc(f# z+^L+fRdsUZ0hRu~^qc#f?Tp5pCm_RmZM6G2YrIBE?e?@2dC9e05Yinu@J`(~o$sm= zOI8wOXn99Sa8MD9MUW?#;n+wzZ&D+h#--7^Z8qN^OB;-vQ#xI)FHEJgZC*#!!S1Db z8uO9q0dME#(Zje|M28o1sBf(l-fFRK%prc_n)XOJ+k2;H)#aEPqA}FBzTlJ5uP`b} zOdlb#CU9P^`O{V3uZ2kF6J{vm`Ik6UXoa@*0imoR;>!u(mo&=q}ZM89WP9a$G7> zm~NV(v#~pYy!qjfLGdX3WY!vYhYfsskA7VPAf`2{?GmEYMY?N)m{!>gxE5gW{Mo-U`7bWn zUeL+jCJ|cepT-EXBaV|`b$mp*+Un81sHW3u7&NtU=F#7Lw|Q)9EF6engW5PM8y;+a z6)Tho2=VgLEj2jCfIqzCa1mE2J> z0z2*&kWZT6CvwNWzO8Wx+g~$yT+ib+F6rwDhVxjAn%&Zh^B%+*`WJ!@7Ms#_&q%@0Vt^08fj4iC zk-o+@dvTur-1gyRQDDR?O{^H8xkMN&yyy}P6dq8vYUWRaYjC%*plEt;gDc=I7JLO1 zPp?q>#zOBsO~Q)mLp@75rF8}_xPOD_U_(5Dq>7PSw}UROLWbXTk@zE$75YHY=i`!3 zN##in1-~`b9V3sRh7@lO%~E7yR_+j1;V|vN36X(|iY3X4UMuUWRa96fz%4~@vI}P! zf&#N&9RtVbaV|$%8|?lf(qaOX_Z*H34@HCD;uixl%SH#V>YIYrFWiMP1v{Z-T^gUO zE-w28dDZOL(5h`FQKz4~GYPQ4y0r&B*A}ad=t9`M&RXmnF}xzw0#G}*0M5z*pTg?3 z=UM<37ly}KH&#so&AE@oausvr__gb-Ym4^yx=HeE`&ocAH-vRystPrB+OTU%X3h8lWy1rNV=flABST~(J9q6L~X2QyNKBDQ27!lv;3`l++OSm8sLtwmXi1`am2in z4XY5~&v5=gt(u?DT1VvWmmzx6u>66DI$dfK{u)K$i>ShNpuz^5m(M-sF@CD)i;I7N zlE4Rl9SD49PRClvBl@>k!f!|V)^w$wLJN%KlhntHPiGYO7dE5$jNXS}?a)lTr7nCM zj4Jgdr;H*U3OZ!-kPsyrgHUTf{A zL@xwt5zaFgZ?FP>ykY~h5vXc&8U1iB=W9J+vK?Ya)OJBbT*YGavN~dUIpZ$GRbE!$ zA>(RqK@9e;WFc$?jYJPO$dsD(Wdgm98K(7nen@42#u|k|qxx=Q1oGm+Xdr>}p&hD& z046eG-qaKGc;p4BJd0s^J`^M?nE4UCLC~7bc2XR+xSs#+liPe^n|iAnp&6aOm2+U8 zblUnr9z{BlNjgq|<>+BLk4xpM6UNS(tB~+$;_6Fz_@&IXhTg$vhHM87kEAjwg$uEp_;<)r^`Mad21m6H^wxq~KCMJ-f32IJEgC{g6-^!; zD}gHR%b_QENl9)|2cwq{OMLEFP3M zrcod)H>+wmtdAU*(_XPV$^HYU-L`T&6387gSq-%M8qF6S1b%dM5Hyb?~cHlS!b-S%-nn76#*K>K4@ox zr1+jhlh@4&!ko-k5cYh1meTEM{N|nQM`=)(AR*Q$`skZ<4x0{bvQjMos4}$hsN>W2 zVerHtmJq0=wyMleG$5RI)%ZfwL&~kDk`;X_V}p@i`1Y}gwd*rMNA~ej!vN5o;BxOx zLLGaaY)T=-;wqPbwV`xAnr2j;Rc02fkD2@fU%S=Hvj}Q}Piy|quKbX2N5WC?(%U=4 z11J;&ohU+a(bhA!VzhlHIqZSo&rT0!InDzu4PdmaAZZebe zZyNP^d?2;OSI56fwUE2e)AA`}K%un&rAzT>Qi;@agW$6l?)q>@L@hNLd2MYz--+)7 zn_X`u*C~Qeq|k==jXEiCrS;c@kvuw`cPIsgclYCpP`PEzjVgB|H?pfN-aY}2((Fh5 z%eCF|wN6|j>pPC)7v;W=gv2A7@b0Pfec!`JY5nSVfP_i9MYOYsiwNOf8+K%HL+n%B zDFfvKxD&h@y&lR40}omKclSw3(yA#ZZDdHYj^@`4o;*f;W;7W#8GIDeY84RogKM7N z(1h;GL?BfE?r*4dOO0-Fu$D38Qu|Wxj1O>+f6ayo!L=YelB9Y0pto}9GJV;#BDVNY zVJsjio#8FiVjs>Jz#&-@nO#C}tG^D!Q?F=l<7xjl-^T|e-Da3{_ML|m^4d@4Z|(z( zKIAHv)nn7`wkio19qv5Ke{*mIOo-muMs3d)Q&ro?Km1KIvL=AI*dA6IzIF%$fyadW zf&}^t6(ocPj%<)qmAQ*OsP5MgnQ6j4Kicyf z_)y5Di`O16DSNF2^E;I;BH^6};cy&M{uWMr7RV<84YS^|Qvp&Il}Nm9Kcqz{BCr^A zKToy$N)VEw$}1qt!mPTq z*$HN8&DL)pQvLpu&-|xZlILH2x&h8y36E|YolZ7^PpCvfkRd1}fp5dHqgqOQV>#fR z#2`j#16-)24+-tVFlaDk4i?XZDrEk9K!7n>C58~RG&3`&7?jm(ai;TMGaxphoE3t0<@ZT)g|M)xsbqY|iZ*4hJ%EO;{FEHGYJRP!Xb4tHi*}o_K z?|)(VAO!KXiEbJ?lK&Z*|NhYC0pm<~3Xb-F#^WE(g7-&Q=e9NO|L-g0Pq#JGX(Rr7 zb^Y^e=-|+5s}cW;PVo1@2@14rZ1jE-l&1~*y~Oa#%lVYccmqS?@i6E1%eTLvs0n=t ziC6U{1(jLD{zTs(^eLBW+vp5J)~~%j7u#CjEl&x)t`2YDye|d z={2yFKcm82Zx5w>nskL%?4eDC6hZ`tStK-ZyE)Bz8w?VB-xE%SLL#~;C6iy@arjFY zD}QMSXE~3DyBZ$Pdv$3$?GOx_c+@V18%*Xh3N0CktPn6LtE@ea6H zqqT9CrfcRXX{eyWoR!4roe)Xq!PY|B{1Xw_K^COY`OBF zq&T5JXjBh=FY}GRHshPWB*Oo`dj}(UQ`P?U3}d*D4zP)>UqDEnZX=_Jq21=t&Okv4zga(hGGOSPdno@8ik z9090&W}xmi^#j0QOFe-2&mH;!v9B#a z4y)x}nayHe8K2*kc0`XS0q9?N$MuQ(xB2+XG5zbCHhxGRULGDkx8N;+Xbx0`ROU z1G9iox~7;%&%R*UNAh)2bGqa8iN0=4ViMFQ%EfjBWoHcb<&{ZA}V0ZPE9 z<(&OM(X`570k*Ro+$KY2viB|v9=Ag*Aol<-B%noM^7U$eO6raI>^qx$>$MK%_nR=B z4(G9~=Ch;giC*lk*L!q`uUJ0|zIaI$!Cg(_7I|1{@Ds^S0g!b~sK3~jY;r!EY-#cM z*?;eH@qqr`a<)$JHu>XXens2jRAQlW0dXtR&#Ahw1JYGh$ko>`T2O$6#z3W%YhU#3 zzj)~H!rHz{C6HP!);+h6aCf9(&^p@S1IWkaf^D66sWL*&U)iR0TRrK7p^>A2P7#t9 z+e1dcfMcqC(W;a#10?VtWU8>4-ixO- zI1GAeN+G^7>3IMex>V)&)}7s)8O${rm!Z>XDnGXvbl3&jCOu21)wFm%xF)eXrK2`v z$uO!!yk;on3UurU$Joj805$0A0Wig{KuPa>D?yLig3G`|`;Tt(7LrH*ZWqaPa#vxz z)PkGSsrociKad}0ror*3(7nZCsX+y=Od&fE;ij7c&J>7MbDYYSE8pM3?E#%C zLd6@*=a>a?ZaT(Vy}azOp4+Z4=r(-@dTwYQ>ebtReMZ79ks;KRhvMH8nTW`BF)zvuePW$l_k3Sg+y2d@ocf%6$ZR zgYN;`8iN0$U{nhL+Ebw&l&x{u1DdJj{V*Cj|K7R?poNqdK#Sm1Kmkm0H}DDoIlm`j zw_1)Bf`XR-RJb>R)&gb(PrsSle*y#`Kc|PlA04+%fFvT6=JPtft~#8p7e2@8v$C8& zL+e%PQfPkhn5)aA@&0?8 z;ESQ5=iE(@5QY@e!#iFljmyDsan@#==l_Js|Mvbcc#u36-(|PDC?CJnXA`DPRrgI-@@T#@qY#qLctw%DgXKDtaB%P3i0rZ>QV zDtre8bNMVvcV$fb1CVcI{Hb!VoZWbNSCG)ws<|N=5yvR|GFTm;qA)8&;Br>B@&<;ivoP1vWX+a}kzoRd8soJhI=ta8SPO&mv zx^vhD)chcXt5ZTCg&@Eab4W73Km5D~!1ZzvN1sO>zeW9GC+JWUZP_8oU>bhF zrd)%$$v_+nv`wV>TY|0c?NPy^l3It#JbSUZ+~z?~*xJm!?LtVpH!#bX^y-v(vgxRh zzIATbrO$lS!cd?SrIzwme*+!dHuQg@*Z-s{fF1B&0G;}8uHZq?)*(iLu^OI!ZtstE z0GyfadKau6EO5pagM`N|9)e0X__OX;^v|ye0q6~kzZ>93dXWkE{3Dj3Z5Xn6gJBAT z6XEFUC>6dy+CHrVXd>2~8x5@U== zm*Ve5s9&e*G&70H3gQo`1Q4(Bg2OFqkHi9u{y)98b0R9!Zv*lBm34{UFJI8qB~pg{ zUL5%KC(*z^0j?h9zeumY-p4x(c64>R;Q%503!`>kSNw3&9_sL;wT=k`<7EQPlt5 zRnNJq1myqyKS8N?Pgh};cZVo*c2`ZQ&0bRYJYLi%{GKrLk~-?HH`e&56lV4kSnN^g z(($6yIRiv4(K)vIh_nE(8OUfTe5|dOn{NC1asUZmXJ!1ib*8lg~jV+~) z-}=u}3*=LjA1^B0I$Uecx!g+UAAF2L-`naH+R8DXH@BRt1#sjNi*+_z067Q*jbT3^ z)g>AwkVv>10?YP)lT#pTt=8n!A;+8Do)_|~tDaT(66W;nUXj}2!}oZ`B+_7frOYGf zqb6=Sf`PFGHKU0DH0JAFJ~C;4feNGJb><4i7(z>&u;(bk=d%RiB9Mkry1*yRM3#{I zydx2asC_3q6#9=z6$1AOV#tt+tIrlnjbCU}Wc#YMb}l)(y~`{BVz-}pb!$379EUM) zHPFm0UfidEm-foMx8MxZJYNF^*gbd<_<+%`wMz{8=Z61$xEChOfn8$mlA;ZoNBEpB zLd`{H{{r5morr(9^LEr-xHakTOKD-9M_W_KK$g#W4!;k8mmdHyYm+NLiE**D-Ez4D zkUkR!)YWqA@`ld=J10=%2ndlXHmX_x;BNQK{8+jLsJYU{@wvqp zba0`_3+oolmaHe~zc0!Fb}A6CiE$<0L;8ONzH5Q4ODUj&6roG5l}C&KvPN8P&q1f` z7v;T%t_;ty5fJVL=Na$9-rXNBaRc5dxKv5*=~_wfy*$>L&8t^W`QE`7u53_i;3y?{OtdlIKpa>z~%Vo*wu+43?cSQoUKfroXLFJc!7byT^7E|iRG9lGhd!2l-nR5?OheIx1+0S#;;tA) zv=O%G@y-ecb?D$<5{N(h(xCMV*E7~YA^*!fqR1g5Bm3as{I!w%jUbF=GhpMET5UE0 zg?-)w9u?BdN5)#~bf8UdGNYst5O=Wn$bS8=a8MwhupF=v36~_E)rrhmiUtMIM0p}E zR?Dq1MBHjptri2De(>8?%X8=4c;f9}h#cR5tiiV;=V}$&NaUiG9c?fElCSbdgh&rI z+egy0iXoSc267~t{lunrD~_8bL5AV-y$dS4rM6cb5CqXD z@t9Uhr?M)fUP%CvGYJl>^=Dj8+qcg&SUSzSn=j{T-GZ?=kEyF05zkG~4AB0fU24Ee|-itHovDptMo z*_ZHJkMM3%jy62&wUF*Gj0E67BQYQe)YL&s>w39MUmPG3)wV;zc$(m2d%3IdJ&`K= zIcT-tpOhf99*4bl2Tsd!WES3-sR0CLqy5-|yK+s(!`ZQ>r3xjnu?6gx09}s^&}vZX zAhjc(kOYd$MnJzrOoO<3I`XW=d#H=A?)B%kNAqkBujVIft@PNAuMX#wejF|v(8`vm z6{{-*BH*n{?LGl(tN)XZS1eF(_{jxy|7B)2IVF7N4Z;cN<8ByBXAs>~B|J-e`#ZNH zKFJKHo{d3hSK5bc4yP@wvzF&8)`vcaj5Q~t`UEWFKtdbx6O>J9H zx-26n6nq*IAW*qdd#AhQvoe;}=qL%~sF(URKuTGuoG&kEGm^?#6${93taJR^BCXn? zak#DQY{s7q{WaPZHE^m@rfA2Xx*pe{ANn3Adal8n9uHQ zCYc(!3}*e$!wOoi3O(JVZN5mwZ~otLjbKM@bV_NQA@jj`{wn7!hI_wcMOh>htoi~x zwPK^J#okh0t#mrGMv{1mW;35)vgH-5!SZO9u+d73hZ(ocmoCX0 za|G54AmK0c07;5PyqX&PqVE^T{9zEA?A`~g>|K_x(&q$q}u@EzoQff{01uyMh^SaV?~wDWA85-bbUlq@#UyhN(~i04f~PpOQ&*FWLj}$ z4K*N+I(F&>gkntU!(0L#zOt*56M$}|jPTsA*)7eNM=<^s23li5Ag60VW59_wwn%Mc zAMu%AztYQFaXpvc##^9RlbPR)llhQek^MHNKqdM6bT03f_=4W%4(gPl>Xm9`fsIqu zc1xKM0ypa0r^^NNS-f49zb2-EiT1BL+M>5N1$nq=1Pr@LOg$`PXXvewG`viRhuih{ zcsi|3peh&5q9$uZ90JRTI+zHQHNTS215{Sk-gkG9M+qu4Y7!YO9)m`mhX|UuGi5pn z=v0dNu&#^FkwfzO-{7j4rI8Y7b-&E&k89-16OoZ>R~m*e3v)Z4W(thPk=bER$(nc! z-Wo)X6Z&{pDDNpkh;4Eq;&R9*vwh6<2jA5LI>oDS)>JJy|l^hQjvj%UISxvui>_1vdKxCwV^{|D%$QJ#iC}f=&^SF3ztntk}$qYCy zognjiydSUDP`Xmfc18TU6zbgvp>AKOE($*TP{$oSr*@;E3ys~t$QoY6kh|IH@zvwD*FA8&-Y zfS#y>91710;9Ec zT>=8qT?V0aNjFl`NP{3q*OuOZba%tO_{VXa&-GmA{pNn*7<)Kiv*Q@Lo zPYu4mjR(WjU+t187F~`s=0JC5y2B>(`}C^R2SsI{X_1b@nKvKIbeLj2t9=1@(C4TJ zoGJr4ZITQCr|_D9;pXprf4iq<1{ORu6VBD%t6!7xJs)|xO{rzrRRQiWFTPMo?!@CG zo@q}MD^P-~1rkaAS@^-RAoKp$IO~??AMR?HmLn*pU99%5tPcv_o!HDHit86I2=z8w zshBGTv#C*QzUX~DYWeKV)`z*%HVVC$w0|9Q(pTDl-9RAx2-vLEXuBWCCQufn%={@z zsx>$eq;)}!5x74~!d*~_$?>{XUdFy84Yt9fA9P%3ikFXPN=7ylc!)=rR)R-;tqBBt z_yUINwJ{5Sx6uljhP>-z7_Bhr)aYv0U{o*a6;NE$ z<#EiSzaFM0;<5g8XcQjA;T)#YaJDzEFz@4O0UIp28IBRJ z&Y*HZ>XKawrqcLeLV&nPt@zXFHE@8$WvV{u@TY~-L#l@{2Lkc+I$vN8umcZ} zHXE3t(hlxUPzIC<&z1OC%g8Zjb4Z)RW_6KT^9#2;6SNF_StLIqN)S0g5SN9#zAu@` zphE6OO66i@ma7u97y?z6Vd>D=KF1dNW}uyP_rhPpaeEtMbj<* z!t%+Be`mw9kz#3uBk|ESZPi;za_kt3Y&`*M%sCa@aiSF77j78V7u zz)i*BVWNsbCPMGsN|6I)eTG9y3`-mDgOQW7ryJXbUrX|I;2P4|lfAVy+7_rB=-^BX zkPVuKeJ(|eA{C)VM#tBX;P-73Yez&ZZQ(BPO-nQr&Hkv1jy5dgFpnIoon&aEM3ffX z+h7^skn~AH@}Xa;aeAn^aM|167i;!<)~;eKPv#vz`#*vhD!v#?nTtv{*_LM3x|) z_B0AapIovT_otE1zS^0PUWP=Yl*x1a2)$s%g*d$nK||O$b(ooG;aZ4SU2d9&i~f7F z=0@CDqDOQ6_+oE{l0uPU-;Ys8vgN(Qh28?SmNR2ISFqT6?MhIzrJga9u-BEXvGUoG z{{6M_Tn82Ve$*ibrG{@PJA}TJLwBtRd5cDx+v!L`>L|Howx8K9@AOcd#HVlr5;(TW zTXJvA{{~=RA`$!gjxnZ%FRQ5IkP?QXbi18LO0?c1J&Z90pV-ZOi$1>f+OXGicw#*#qvu&44UwQm zfS$O?C$J=c(EeIinz%2%`W00)WckK#mtlW7KhzES;uDXKn&<6y0|*@I$v!6>F-xkW zdVmoHiLCXzxnvX<;hBSH?0_3rv8|Lr=$@Pwozs0W8zmRd=&Ybid?uzDA8s(yQ;chw zzv2Yp`4BXE1)68<{+EU1pDcG<{Zt>?8?pI*)QZp9dDy#(&hY1SOol$gp_dM94-wVJ zEq@jCJ&V;;G#;Z@pIRBUM7QT`8I);|g}48$f*Ox{xVS?{vSsiDu4HUMqaW9^P(eD< z4|=Hr?crm z$xL}aZrkkJROSFUVgXd8J}H8h^y}21lG0lTf}_~BHQER5U>4HeM50>;8oTlcLB2BQn5zq($?|#LN4E)WIz&JRzhZNVo5rugf;Ip@T_e%=SnIyzcF_)U*aWspY)q2*qVfYX*Oa| zdibmMC)E^m=W6%ruVS*Wv`zU$p^hX~ST_l^o5U}41a#LpWzrBqTzVT_JBS6HTuj$$ zUJ+Qb_a})+;@}``%uczi7W(t4K>rMOD-r*S|D|Q#pTx;jfk<7I?Kn&-J}#}5H&geT zdBI_LH8>1fEgd0u)feo^tmxMMQwUj0uXJmL57p;2;^cJB^0`NV>*=t_dsJcc%{8)#)BUX+V|#SAqBK4=e4 zw#s;I7e0)IyYY!8-nDozNh)0!B!f>F8D-!2ZykWcrpZRsc=rL4Ule>9#s8#fi&31o zU;2^2gCfZutiEx8zi^-iFFPa^P*ZJ^r#Y_;#A+R`{VIpJ`(B=^_I$mgdVn}64G)YU z;wnDGqeE*_dAQ|u{I=G93i9ZYR+)AYA}1vWBG2552-2*odUMpCKcFf*@m?hDi)St1 zag7Li4xP_z4!SG>dLRAn+4PaDcZ`;M&mR~+9v4?28Y%u~geluLhkC93lYzRvWE$1qG1}1gP5E;1 z8#h5w2VL55M}x`KK;3N8xbQ!=Sb?^LDj@ceGH^(dGEHu)_fLBN=-qFt*RgY(S$W8! z4nnwvZmU}5hKvsE4#noav-OdvygX}};-+bTh>q&L$Q+^{GscutqLme+q`U@tgT`w0 zWFWCCS<-Jw=E3r1A)}Xdny}8iE4((sz)XbU^`Oa#HDwOZkYQ9*v-Zkmk?E4(WuO$5 z^tspLlD-A1Yw=A8f>z&Sk)7~ibjvS7;sGa;R}{zjo~VnhleH2r1Hmq9W*mQl;XVzn z@%Fc-!?YbbmskZ#YlKky8BKPMseNAxZ;1}GI}oMSYHT&*F^4>2yOrVui$#XKP-DHJ zXrFNivpp)85VMjweb46Hxoi$;Ykrl4B&Lb_8&Gl!#Y&49YBebq>7fyp`Cf{dKPoM#}6! zFX+;)OUT_nOkG>^e`e_Okv!E{sv7*fyX1w?0{#r)I5qCDpBvu%d4CPdW1^QrvQmb= zeQ+m`3he3m1y9-YO^EuY2JV`5^zcWc4=JV2j}_J*jway<(YQ()Let>{Yyw1LX5F9L z&>hgNgIwMy&hJbhh@3`kH3owlH94DH8yQp#9e+BL<7ynWgFOdcrh8)h&1Cr@qhV8h z=rAg~3o_c{PgDN=eCTtoM5n;SQ}+sy-he@o8INB6DIk*)R+tme`vpt!(hriWEc-wm zC=2vB6EEH7{7b5^#W$VxFtdF@lpS4%><_-fet_wRzWSnEvo+4Q#;VnlCVe-6vi&u4 zZupw_vZzx#!xUm^2|o3~hfSe?WoCRd07r+*CShbAJ}z2P6(S^?(<6rJ#SsIY0kYsM8GW6vZVe zSy1PL!YCC^umeS!uYQ8i;0eZ$i7W}io`>gauB-ndYIRgNqy5SVY;WV%m%gyJ#>s!t z`9`NmkDA@15hMVe-WB%!nqB(3(Mdv@U78Y^)}4!86Z7&;6m33&rykH?`wh?#?h7e& zL9BTZ0=h{BM3XcCpP5`ggm&-Vfe~&DyQ4^8>mUXZ*VD0i4iWa<$~dAyP@d)5B{YsXyFCCMDxEYH;S9EivF2ivbM5)qjw0uP!Gh{k(J{Rjl|<^ z%>9@ud%vuCGP?3fSWYc8Znf$jTch2dg1%hdsmikYL5uSSJY@Z&U-D^Q?L52lCM;TF z5E6*Gabxk{N`#=O2fXt^QLGC0skZ3>4AfqxF`fS&X<&+xoy7IeJN=JFcu)PRJ#`|j zr2jAV`#*Kgf8`N4HC+7eb$e*a$$vGs|2)ZG4?^e=$vJo}{x@wKJdqPC!gO+D0)xW; zu4qaJ1o6*9{~0R$Pl5OE$2bs(FyROfwD6nkKWpm$^R)(Yr~uBB!yg^z|M%7X_f?C% z#My^Zi1@sW`^EKt<$v#YwUml@`@sAsS%|m~rZb*^YHUzpH)@G<- zt;u*2n4@gv(vMv8-k3LQr({LCdBs4mW61o`c;jjC-8J6L4%XoNY)b6^e6D{#$2C-s z4h(MndZ)pRM^b{YuR1g!zcV(g6)PT0j#BJwCYzE+J+1&((0?T{(+Bhu3BAPM6#w^U zgC6~b-~9o~Lk3w_xum})@#_~^sjoEvXm?U@rhBKGomRQrsJUDo7(T|Z z=x|N6+^d)UMBI5`?ZoA>s(24ZU{HwYQ@@A+%RaW);Bc(IRG4F~{>7W6l@&U$|A_@F zl3##iwCEWmJA^Nw#4xJUPcZ1?dEcag^-Qi}yqa*QcORf_^T1SCtw@dT9`a!^YSF(Z z`vhACr~p9n$bHn7$4Iv9zlRx6Qpo}VK8of;4{|macBZ=@{&Vh$$ zJ>!qx{H$stx7R{^$E0nKM%hJee zXFzm_B708HY1XA7QAhFpws3PWg|5Vw`?3PCBA$1<&0HnE2TP$%J_l;#&wXg2`vHFn zGw$g-qA@F35ZQAtp1+iks4lmO&A%sP0R0yMfE0-u)h9pwX>sIH+Z4S)h9#oW`}WVT z$_-hO{D}xR5i8T4xhw0|*wO(C9|J3bt!Nq0mO%$6V2b(nm)-o=2E|mNf!E}Bq^@qB zw?)rb)_yqN;-vx^86GZcUUh zOjTJVHn^OC=U zu@3Xzc0<*-w*$J2y48iQ+vBSAD(Nf>zGq)FzG|p`S+W-ey{aHF>lVRX;|okT#V){;>^pKT%G4{&aoC4`${BnHlz(2&=2imi^k>l} z4l9ww!TVTxdBEtT2{An7F4eA-NEh`k$nSs((rG**VgsxN6x{~i%iiqOGozHOCz)NcJT(KH=a3hY~b5TlaTMXkKEIZ{NR(N z1{*KDCiO+K4;x3g6}%5G3#7s5`jh9;YVR@8*o}*Bz~SxLp8uxc4Y>9O46e!jb0@%2 zgL=TFuU!i250;AQXTLid*)9HDq>-=44F#PJ`2ZU3eoX2TN9okLumvkerq9uC^}lQR zVPoWfWAi3#S*OY(9{o=L{2FJkKz1|J|mB z_R)!jo?AAB1U6^g^{^M>IRr+hb0$sF-juW9@4s{M+K<0HK}UEkA#V$s{=m*xGI?r? zuUxlT-iB^#=Tki(=Ig~|mpA{sKFcb(TH9TD!lWTFmb)(yLc+PIgZzD#38^13FXPxHN1~YHf@?D9gbjl9|JF)8VogAjJ z`3e#7*(W__)>XVn9$R@MhnN-lAjE+~^{i}*b$vKjAb-^T58*d>RKNeJz{ zyE!vjW?XfHt~j^21-*a9qu>^FLFltc*=|oh{c_q|f6e)^7_!?Juvf{Rhq-@A

    8+*>9+QCAg^UyFRU7HDOMZWHe*vW^waJV^{rvfZ?1t%IA5Y<4w_xz&XX&Cr zUd;(G4OZ~)L^I3?c6M2_)pEE=c8=N693Z;92f8bwS#}-%^!UE>ez8{NPLa)OUEnYB zTU*o!R_<@h&!Y`={jL35De{}^b&*ZFY2S@eI`Qvr6ku@Q#p`}dy595@$zl=d$>5RW zH+R+pmi0CAGxS|fy?d6n4;eX1=ctN2`5T^VYP^=|eA>M-t8=||cTL+Wnl|$ME&U58&H-xRrOTE#2 zVQyw*^e5fkXlG?cW)*|soM;wTZhODKiw?6HV5J+(26t~|%|z!l+ZFf7>a|)45I7v! zVYpffOA>+h0rkDF-4}sIYfJ9!m-C&z1v)vxU)tIx_dbhYk=H*Qv@Ob3Vo*-25EMZ< z)qy!I#r(q->Pxq=`ZS4qylNHqO&0ZUxg4q4CH3n%z)fVbU^@BcdNjv#Z?EH zxB~23zgv7vh~Y4ixF57DCJBg>+PsA$ukO+KD}d>G{3`ACidMYYJ(Mv#ZF_*J=7V)?+A()SGhQDLf&fwD?!|b!{MJ^i^ZT&zAd;iE_4Hu z=^6Om41{Rs0e!L0e+kg`K${EJ9A|UK0IM6wj%67d%{ACbcy}aY;oVDlg=TfTCG9N zqBn!Rm5ZHU`60VuePN$;2GxGJN-d z{%&)wsTGc(?7)_S{%yUV@rP(8s7A0|Sg#)V)-_ujZ_axoA1J15!b;fdUjT}Npd8om zZK%Y4Rx*t@md!|0sOfB>2@PGF7|AEncocDxYr{at0b*V{^=`#J-auyw+pWuv@^!!> zKe6(4sBXhJquVXT{h=T`wb#c~1}zL8uQZdfStx7Ph26oUaeOF44z@%f?A3Vs!&>Lm zWCt@)gOIsAzZSSV<_^=R)&L%xQK{GSO^kHS@@SXgS9sejyDG86NIg!<6HJm{z@ZY23uFnp{Dr_ z7&x7y=oI6t%m=f(YwUHZlKpccJ&%PT9v&GzNgup%9mZNWe&Qur8NmMITB%P72>*Vr z8PJOZ(f43X7P2TN6@9CZx``yfsSf;XSs|3V!$B?_ zpL=MnJG(Laq%fE9AgUZYLhiR|T$OW;7A0-MP*gMgzQ_{`mI?A>&z0i%K6d(U;&W-q z03s+$yY$`7wu*zU-(c>4Hs(veryNrV`N-ai zT-M=|FCo2i3%XNOOag`|OA&}s(th-N)I8;PLYO;-TU_!f^tl|%lEi=r+|=3=X+E*( z6MdSkVfOWh436S_+svz=PgzE-ejlEftyM1f#Hol%6;7hwzSxO}eb;iN;}EY%k1bF} z>ud1X$adWw*CEJCn1m7d#e>2oRsd&n8O?7>Unqb-rH zn+Gj8!`oBeW#+=AKR&6k&S}OJHagp#kEQXa_&lJ;_shutHqWnT=5E-q#^pF^q_5xW z$D51=z1a+$F3>xn5S1aowm-vXwM&{Th(1#DV&`^D~M+DCp5@{j6*EbHv4X z&QjmbdO!1v$0R zev3_(Zz$(V`Hes0DGpPqe#7W2GBo~0G&yT5y^0!)Fr~4+d!krNeF)*YYqd93rt$5D zExt#m)zZ&LG#1nw78Hrl+<%x#xivc<)cGf_M z* zxmG)6c6OfCmMna|JlRV9`;6rgJ|GEOc&v{X;ZXkqXP*jrRR`t8;8Z+NHaKu2nI=urud`cFR!Uf8#>8W8Qj}PR7?o2|c8Vkk2*{s1 zcWe1uJ5WAAKxDO6S4;btcnCJgC2~kDOYIDJ^k0$)X%<6^Ogh7YP?=iBv5B~(l$=s| z&qgd&lfT|l3EAG0OFWTst@4@Q5%~g3}C0Bw_kuRi~;({VYEOM zx<4*_Ce+il26~zz^uLC6gXobI2dQPESb)-}E-fw_6pexd7yIddHR+JY7+y;izt2Qv zGnW4ZI_esP&rEO5p(;6*CoEsRzgm*!lp}#>Lh@3WeWGx(OC8wS0~dp@LhnYE>96F{Kf> z)W_dZ`@k#Xo%|QcJrBowG_Ya6ggl~~4|!;=SMLpknh1AUHMSFKt<1xxBfXcG5^aw- z)ub#AOUg0({R@;*^FtzWma?&Be<(cWPb~j+WVZqHeYX|dBI3PEqbU8QyX~=Gywz;9 zU$)O|IMg-pFjFaX4ZXGMDIUFvETGObg3KVGw&+#v^~I4w%k_o%XqTKVNr$HO02Sr? zmauw%Dkw;n{v3Gan00G*ZlearHjd_>q&ht@>vA?4Odg`NCieuA7YZo3klXrZH@Q1Fz@I{p*@1a%WTA9=DvL^0y7 zVc$j~)a3v%n2$=7p31{hnhULq(!Ptp`X=V%a@6Jf^>>q~^!n$L2F6y8nRM5Hei~wq z2rYhKf=$5(f&1$l^`riuPMdMmqM%0~>M#?;Y?wX>m2CF{^j8%_D<@l< z2l2_-Ukf*o-^7;l>uXhv_+<`eJS+EmcG6{-h+Z}7pjXFe!s{oaDq3AhO4pi>v~|>S zck7*JgA!{4sOS}2L7=Jt#VuF<~UtWw1Rh9baI(>}x$I)Jg>`+5HBIVH6 zBdkru{trv2xw9P`O`wk$o# zS3g9TiPE)bZ&c?+*NA8GUW#_BTU@ZbuCRURrf^7?r<4KC0jWwQ9{@#$Z52%iN2Dhq z!JNM@D>yu^mImxI9BJ>Oe50mU`k&`9y&VyS-hIwoxma9AsUiPu^_&By!Q0j@YSqIH z?cKi?x6`%T67nu9rLSK)``M^(JydHm_riukLJxnsT-W$r6S&}%ZY}ctgMHM;1n5Du zoGik%@c}fnP9Ko?I_W(Yk&u*9E@$$jKQs-Iim!j-Cf3*s%c1z*#@NfCm=`3JH}uJH zqNqkItrVM>Et=x_s}z%clgUC=nyN6ezJLab5MoeDH4G_bvKHE&YVOH?m*Y>NX)!aJ zUHQ|x1_4~XaLWPtmF&6myQPyUe26{-fdip4ge(PPSWzO#hEefd)+38w{o`x)usa#i ztiWcEUQ>&C_rpJO1x)QBPcTX^Z0^!0b>#*hf7tR`I!CPvi#!p;u_Vm-up|(YJ4g%V zGj0h*<@1?oUkR*Q6H;&C#JeKLLL2{g9H-ffKqs;w5h&EeAybt0(tX=yoXu*tsO_}JLnPhqdXQG-v;M)G zA64YTcDDA3Ic`?nRiOT|r1IDKc9wqYU})R^^obq5roYD?O+=Tn)-}1#oShed)p5Q%j-8b(mDYSsN?bp;i}_t7p!%7HGs&cY0#&zYRrPD?;9IfzC-G6*7Qm}2?Holp8SSNwYjIfu0QA!6@dqWfF! z7v=M&XEFw_f5B{(-$BWK0 z_HHlb(z0KWk+~TMsg+;!*DJX7 z)x`%POtVfM->Dm%k>J%a4Ai;rQ^f|VBr~ct~O(G zH(nxsT{ynO<&Znx=nUZmFddL9OKTVpAw__?N>JRMFJrH06)0|S#qcTVuArb1D+y6$ zLg>dETvh;M!P-CEQyJ1`0az7V>Uc&o`aB|}MZ9d@%OCR|B9ol${^tvl;?KCO`o4l` zp|P=ob@5QBF(!HbCn7 zz@=ZC7F|T0vIy=bubDzV>`d3Yu9`QX!sK#%L@I-=KPvu|JMVH?&voPkJ+=T zSvZxcrW*MjCPIDR%|}!sw_rwgtp0n*t!R;anTl?EjAOeb_iNYW=mzMx zbl$bwYE9bCZjMC$3)|1P1}EEn{?TsWwup<0ewrf0t&bZT5fgwufpdszd0GQPtP$8SsR;{$7T>*Us4P6dZADnnk9r^t81B|T1s-mX6dsnI-^H4Wg}Z&StL zzg>OSlzn4tz8%|~-A-y2oO4CIvNSE`Vfy8|k-zjvaQa0nK##M8y?5TMroS1dUj|y{ zA#j=-v-QugxR&eH%fiJ+SrwWMK3hju<7QpKv50Cst_T}Rh!r6!BgSq*= z&u&*_4mr<_bF*)CZ_+ni9G-7o5+<7b6}~ba&K>btLjY9|mflQ<;7%>pp8CuHvo;hm z8z*K>&}7i$q3SDW1gv9kgm&{20|%%Bjp>{YI;4|9Xd}jrn>C2HoQ*%(ozP2`xP-Zz zIMv0TAd4d8z**1j?tFMVu8Aq!K`Y?R1#x?dT$?nArgM2Di!TYj*{6uvT%j1GJRIZ8VxMB#Y-Pf{WKF7r1zlDjd*|O}JyGaUCc9Z- z;H`dR-1@JvKocb4jgK&Tx;#-zkVMP^FCONBo|hOo_g~5+kV7ee*wN(ju>O?+71zhzf1W4i?jRZuil3`)d0?9?njN74)H%rs{bkM|NZ#? zyKi1hU40(u|BZDa9OZ##{;#MD0&XUJz!lr)`drnp$%9I$V=I;4@ehC*n*N)JB!Lih-LP`wX82~pntM6Z-a3qTZi9d$}s7e3GgtohBdmuYS2i&jA<8QC*e zSC318ZRe%CKW}h7i0N$V2fqy`Mg(z3iSkdFdv9;94AhMYpWeIbK1_i~7=njhByYag zTb+@+eF6DhORfxvZ=%LRueg_iC&2>FjH z-9tygCf6ts2AO>Zt-#Gx-||c+w2W9 znJV*%B70@o_lZ^x7Z~nR!L+d>d0Tl!(CfIcKlPakKD+MM!{v((ms22@*X*n}eD_)@ zPJ8A(SkySnh&S&~%xg~zM6Yi%EPBf&b9nk5I55whlo~z-Vc*MYI8nXJRg_TFR@lx| zl_l7I>sf$3*63Fo?t95e7Z$qi^Zo55^wo(Uo9Lsqy|v$!W)|1=2diJ|jexC#5tNH1 zf<4;3XL_IqvGLn(m(oV<@jD+Uyp_O!)8MtgVV97<;|Z|cn%!0i99rRU1*Vp;TAQ&K z3G7D7V;Zeu&Z4OGoCwfCPEWpkX05XOE}qEm^{;~;RC}=*wb(C1-5Z_-YL;mAcHa4f zNS4`e?V0%}(kUd$W=p--*ZKOZ&vv*X5Vsq=p>aA3{CnA4-5oyiZx+Bm5_-&`ggh09 zjz|DrXu)y&YTlMc7YY!{P`0~p-}Amm6HW)*#iMm9Mo})e_13D{QlBk;B^IqoWB{8l z=Y6wl1PQ;gjYmG1vQhj;((J%Mz}|uN&v^-3!zmm5_gd~nis@PhW)R_0>v19eJ(1NQ zUs1yQbe&PJ)?U>>z;*o-=(txiM&$CnbUisc-O*cV#k2s_meQA;Bb4fedk_QIU~rau^mFQ`zuR zr8SgamNE{n-g7yBw$bfZcH*#Nz$9iuH)M=DDR{&6X4$Tmb zcVjN^QtE&<-LmS}bGnhtIFy(C zi-x0T?+)~z^AY)rI_)i9RM6qHxbLNxS0W5$mwHcVV{dkI+dk)8Oxdi&OG&O@`fbp_tgnZbs4k!e88piFwiSoiw!TBJaBgPRXd-cx034n~es-+qWwob4SB znvL!Cm&zQT-k_uw%=;e7N9kXw7&cY*tf9m9~3-embNFUx8F?Mq3|>(MoLjlG`3k&pbV zZ*$VP>sU)4kemh6bev%%IH!Dv@RWq$z$cZnoh~392rzD)z_bFvb=@uX7%= zzS4NJ+E)Z|^~h?YFnoFte7n9#a}rJv1Q@fYfYDQHndK0)2J6g>TTxPz9$Je0hoA!d zCDRdziNl93MW9Yo-s|T%FQRap?7KjyK{Xg|0OBwA+hVBIVQ%jz7%F-0?;-W3vX@^5 z>9-pCek33?OTJHOJC}lkkOj5Tl3ZXdmO2F5`@CE80Q+`Q+Dc!t0&0us7b>+ZA7mnK zuZZb;OpZv@uPG(XTsA}en4KT&Y6LYipkV+Zb1~Mc?yZ z#pBPzh7sW#V|iLVfFmkY%;OM0V$PbsM+2r2$F9}r6c`aw2mQgqD=?FKtxhoS)qrjc zr{oKZx1k5v^xD9PXe0~Q_^Qq6oALwKjPlyTbX#)9SqUzQ>eqYtXOJeIS?g85-`$P- z$0O1j%$-n1^~g=$T&V-x%^|Q|@RZo?KM8Nk@5Ck>Zyw9`L_e8%upl)^dp%ffKcdEE z(N8;3{OV`|<@me88rx`&47R37nV>caFQfAS92?VOyi`xiIqbB$`A@$fPHjA+!Hc;P zfWfJ)*cxd8$V%&K#2xy?X{Yh#w~{96B3&8(u5*%ZPBS>?xcO0hJHkuAd_cbT>(uxS z=0c&}t8&h@_Nq`TMFz!8=N;!g_}MdsRhG@V-{tT9K&nDK1)oFlM!#W~3@W!G?J?QeFtBA$)E;D%9%!O(lSbxszp22= zTK*rj1@A4->)GGJWx8wbQ*Q+_Vod#?%q&Qo3bO&?fW4jn+(a0YfGzE^xq}cA#_^*| zd$>u*1wBjmbBDC~WWNsn$=S|~R`R9pHjjN!#bFS2tfMY(eQd zLEHt8Ld&&d?_oEJaGeq{zv|GB3Zw9iPTK}hpvU{d_?q>~K!>+w*;8D?*P3RO9m(%A zh%hN~6XsuMp8qkzcK()+9*Xywp@O1Yl^$ZcfTc}ibXPCc7d6j>`kf^qPo^xTB)EcA z?{4DI4fZ3(abHMo+~U2xz{Q57&b;`R`84@hlC==O+tA9W=LnS>+bOn=zr+WOnWsco zUt=PCwTfGr?=Ejt5!NuYUG%@5>XjaLZ&k~_P5VwGsU)%)lQNySl~@z=a~#X;2B&%= zqgKYWI+Z~>+%ZmDl;soCa^0_+f{gy}&)9^YvfiC5T$YQlp*~}eS!%uLX9pJ+jKF{L zpWEfzI>*MiwV97qEbi3+z!*PC3cu!C;y>X6TYw?#tMupIT_w4R#-^&k)sie=lYQCo zDqoR^@nuDTSp8&?3&RNkNLapTSQz@|G$N6gHF>ty67yW}%vfGv$-$6`UYaeEMx zU1SRHeFh<-cmSt#B(Y|`;umTVFBU7}dj*BK0$7OKQZ=X!j6vVk)zztW_JlYC1{vS8 z9#}8nsck_Q=;lC~ezUqw#OoYtjMie2rUc^Sw1&(94{8n=g4Nbj(P{#afOEtbB99|s z@=XU+tP29QzqkqrHu3?Eu8$+-jzJ&%giEx~K6kal7N12_)LRo0ys~>0{2m(ssx@ul zLh^XCo;w|(pzHakYu{r>I&)c&fhXj4pX2H^Zk^2Mn*tZ;&!s#j1V;0L?5vdlIU>lN zHTv!F(Bwkht5_ZDBH%3%^W_lm+Af4U0<0j0$41TKMZMR6?R(%5SweWLka(HyvGpMf zGotbmu&7)<=T3o6=*0b?&pm#bA*#g7kK{IWYNI))cH>QZgH2U_@odf3WN|dx?ZHBg zxxl_GAJajxsX#ZezRWvoL?6qlslKlu)D3HfPP zn>?8_^2>@tOg@Xg$9eIXSoN=kh-zexMOd=rcGgLc^MEa9ll+J8Ck99Ijp@9S%S`oe z=5kf2zu{ED6VK~e$zY-UH;+6i8TAsWdpm0|J zhiBV*B6_aU)m&q9M1SAMO)BjA^3BR1UG4m{7m>tyA(Ka|L+ZrbgYzf??2_FW_hulA z!;SSudbt#-GW{0QISo3WfIUX&xNA^!1>iw@FWt6I*iJ?;J2-&obJWuI70yoq)cuLt zof%q{bYc0IS-kPrVA-%G3JC?v2JbVrT{nG~+{jeEV(&tq(8Cs--^511D7Ag8ZOej# zNMx;9s`LFtWUI^CfM&~Yd{%w){?$=8>yPdIJnE~8G3@>#{)uzBa(EW)#*g!Z?>a*1 z#hr7)HdD^PHr%)EccVkeeL4h(vVN1Rs*wBU_TJ?&YDNA!Z0L9dcC4Q%EbJbc%2PYs z7lKpOclY}l`uWo`whu-UPVTJuPnDhOvRQXSO&y$JYWkN?+&_H(9a^1#NW>{2mAYh!^V|8>@PTzj5RDOrozy76K;31c;ic!sp2CSo(W}ywIA$Yd zia%mc`7o%xKr{t%>&-$>x~_1ae55vmp>xj9JOs@@5_f%EC*eM7-b?s`b_Z&~rJ{aH zn|}^s!f9G#?ShNt+u4`pn{8%F=k2YL#F<@UlHO!`>kz%9BcU(C)OToDB~W0Fe&NgD z-I$Qz&uZxOJdupY@|h1=>qea7tw{MSiK?XSnlhCey<_|WwtftAf}2rw1gBXx;i&oQ z_aM~)tG$@gg1h*f=DuHZixVsM3-WIcFOe?%`alV4CE53(g&2n~EuS%Wdj~WK@8iJchvkJ|6Sz%mViq;S^sXTXr4GZMz{TnF-US3N z)s_)>vhU|~8n#vO!AZbugE>(7%QMuDCioA;EGmTr7QT7@$uQ-4xIQ|8NLqRh^dsjj zwRA+T0$~Q#tmJUe&xw2ok|&r>yRZ^17015Miu3k6{0qXnT7P6HHJ~dIETTJXMY*cp2ODAqIa#SfJJs{=f;L_#n5(11hOO91S?@yX9Qj z5$ZXg(zwAvtQsAukHBZ(d|3m@Fq>@mc&n5?H}(RNq&#Q=S?9$0x($kL`?~7 zTyp4qUYaQsxD`s;Od^o4Fl_KDFa|$jiMkJ);R;r6W6po6SCyGiK1S+vMpAM1?tHsQ zm<|Ff2rn{bpJRdMmT^367^2guU8{s}%N@Ms2FF!YLQ(RKFJYGow5n9M4)?md%@KuA zO3DL@{36KXID8orQE{i9D@zqg%&RtZ8%g4mba^)Ww&rGc*Gj90txWK9j#T&XNmGv? z{OS1Q$jK9!(w)A78(qR3j0|0z!Rl?e)<;3QXi9GEum688v~^)C2bmA(*#3z6bz(RXMC?xozEFfooDpX- z?%K{KHvL~t_MQ9BO-R{2K83nvd9h%ah5ghodCU5oz|Hi*onHM~kbdKl)TL%06KeK_ zk4!>uoHmzsSLsT)Pe+_TQ?lVSz6!%)XGlWQ=MK5KCPQLnua(wMYF~xit_w#COz{tH zsl$98jaiX!kU210%2B72XVL17YO8ViN=&)j@>vSj6i>z4&Oj4RCYV3 z+0bXI2#Cr9idlck^Qgm3lKM+Gs18$thj|3ekxkTS@RlkM?db+0iiLx$JgIP#Z6j4*!L z`a!}aWe||dgMng_P|qm&jtau#jzKL0V>3X8QF=hcDmrQ~3?Jk4;*f{BBX^=)hq`Q$u z0qGKu?vQRIrBhluhHm)o+1>Zu-QTW`b z(5~2UGj3+7rp#*hw?vX%BJM{0 zg;vbfNeP(ovmei-;sE(}Di}j&4&v$Ben8IX2WzGDB9pjoRbO-Z=!BK5|DCDlt$PrrwjDVKjx%a?q89quL zef^e&OpK~M8ZPd412a;}eT_3?Y=gz;Nq`4mA;RN^zN@zQ3*Ha+Gv`y5>%*nD`W8t? zpY&_jMTgKAbWdo6865K``=8+_*F84kxOe0zqx-t?d%Vv8T~`I?ws2<{4R`Gk(L+5l zv#`~heus4URLrH42u_XScbEr@#b(EMUyIR*vN&#b-oAHH<2L>Cy=?6bB9o1+9dx4SAEMalk@ z+!r1coJWbPemsa)@tOM$L9mOFh$rsHRg+4UIK2WC%|6Cuk@N&~|Pp zh94Xq9qkIO{}8Rab+L}IKcDYSWeq&KY{sePrJ+tjey7|OjTD5I*#y!4Ww&&KXazf) z_^k&egjGDfusUC(TLJI+{T%5~PWBG9H_(9e4#e*LccblY-yV0vh*9@sQfcWzLxB2a z+B}NX(z$_xaI=*?I-%p!I;(_ZW@>rw?t{{hWPj=lJ}B&y17(gAguJtTXwi zr%cJH1SDaB_JmtA)umhSKOpFS8>Td3(9RJM&A@5&Hf7FmcGSiz<5XSnh4ch?9|uMZdi~`>{MWK2080b56rM3!!WaVcerlo&55+Lk1jf z?w=?=a!a=*?aZzrRt|H%I}dEdY|TmQu@gWkGUtpRxO;xEMYq@<7M0l>XNq$BjVr~_ zQ0CKI{ICnknF_lP{p@diZctp#6DRS`eb^CILDz*6g8`tw7cpO<&W8?06H0c&sulrq-@NCe0@)Psy^;#-u6r9POc_dFRrHQEo%Cj_(Qf3JJlh+iO5w~ z8kLG}Yy;L^_6_&z8CNyn-UOqc2jVBILzo>;0O~F5Z2N>dQsx4)lf-DC9?bHh2=$Ig zm%>8(2DEXCs$4%RWy!}k+?sE)b#kC+18B^P< z{A}@)J-MHp@Ugqj;#fue`Qq<+KgeCXB{4o2|MA`9QNN{5pe!B4@AE{)!YG0Y`)f8U=Y39Z{wiozm<#uPG!-REzbW1V)A?zfbQ6-iKL*CoCQI>?nR`0a3EvboLF)vAb!By}i7=4al zPq>=f!{fU;2}}-xDc>G=QZfTeU#Mr+1!SPL7h*a2HXWI)dMF_3%j46Ml6(d903p(# zB@5ZUa7h$&u1JBP;Via)iy)&$-|-pzBK!Ctuf>ky6Ml%qF=9!CgBw1zbxqY@4l=$U6n%Rd8A0S9JeZM+>%qfXFp)$g6yh@t%R% zlkKS@A1?F0nfZ2JN8%-Z>U*G;S_cdQ8Ou;gSxtAi2>jyvRpz+racNe69x6dAhfCI6 z>~0u*35Qs87o!OWb1Nh#r;j?|v>b<#R8LL3R;-~j#E{2ft%;LpD$Ess7vGmVoQ_~N z1{!OPqcdA!I?)y9jPE+9>S63@i@HJ_%Io9bygxkydyz`^9rzt8b)wQQEs2fjlzN2x zn!9x?6}s4Df1f#}!!!31O9*1uTJeZ;(;jjUA>`k2o7r}`hk`nIX$LxwI@I_MfG5hC zRnbkHqpFqe{UFa}XWQ+_>>Q`xRne+rS8wTInReg6AvX-P#qMuI7pPixL(J}py?9M- zb7#TR7)bWfl%n99b7$CcJi*5?*Ov>X*2ML&y(W#GP75Pu57WWyb(5bbv2@Os%ebwbo~(L0ANUH>om)hK^4WJ8Wd3>AZ#568W<{v7xhMK<` zw!iPTHX@j%R%>r)dugnj@I^$tSMxzD(+i?3{XMg(;<^40M<%xv_0JREx0C-q$M%$M zB$QiAyzImu<~|)D&xwh{RXX3q{WOcy66JC+-%TS-gt%o%R(pqpRV`0g0H+_rr+CgE zGD#k&rJ#o8M<0HjaMhuzDXx`A^UMk<8b7f$$`0_SV)VNw6DwjBy}Ky&`s88W7Ovch z{4ORh+EKOo=F}c1AU5Hn2t-Ira!)HZ<>Jn0SNXx<0yz6{#d(*93bj6d&~mfcz8GnO zy;%_igKA33Oh#`auL{x-`xJXg#hN;BwL^}Kl_3Rn=f0wegcjHIjb=KOtzKR(p>*3T zVM}TaVDiYT29;*hTJrS~bV0}q_P}oIIrsQg0P6Fn!l!Qe&CurKx3)gA-`S?+7yWP? zOU@3q(`Qtw0@sGWMpaX8I3HM<<5%3doJYB3sT=}j@wtM@K}Q2mJL$*1JS8=wsR{&A zvGk>$h*x96c9V+kj;?BtOPm+`#y3fJ4+j!2!*=Qc3b4*IFL*>ZNYDrb-7KW6n!`cv zd?cH={Pk|zh*IU6Ry)d1Z|C_}XvFJ<6-`^&v5N8WgTtF|;Oi!Q*6xO_P*?`RQx4ZB zp0+$w5Pg4KVBgFjMyjBw55i&@`_gnEXdMq;Wn~uy30OfNA?ku?#h|P>-YM0NLq@pW zw>X#5#oDZE?G3VFp%&isl=T>Jp1CStguhF!k+(eF0H!0pg3Ha+5w|<(%fCX73yzq? zf~@hHb9+C-KN2LR$lMQG>}1nqY7ZsPozH47Yjp7aVGk@34a17(&o~WMp=M)Of}j=> zUal@p&S$dULzk3+k_5{?yGRo2{6;6|Dm|v+;6!mg^3&`==sB>amo?hyadK_;R-|e< zqE26)7c&=&WuT6RjgdDi&42fkf2kO6Pw!6A$`H_YhdH(Wb1kP@VfRU2WAen?e2c-d zK*z}Xj#EGX^3Hv~Y)Gly)~FJ#WM~F{D7%p0N{(X1SGJ%Xs(?@24Y^-eaO;oX{^VA9 z%6%MY#$Ee)w}{k>=Lo;RY)=@AdBB>UhaZ*$)$sx%;Q0NDJ?N`x1bX6POu9DoN{i_>m{N84J2R*)#PJ!g)uNyT<%< zMLWJ;XxU#hdKYG|akv}5bdPI>VP6=CR(InZ!X7($H^S(J)4^(3HM?QKX;+D%Ub-qmsTEiU?t*$=ZK8awXm zI1IpOLgu#+#T^EDG;&MUbUQt77j+bE-i@NmZAKtw%%SUdeg(HY=eG=>m1gL;d2<>u zJ}WO`?m_0`7>aX_0zZJV_25(XSTx<-$3f{8=pS%4$Cn^Tclzo|IepTo@jVHDQq-fh zq~K)gcqO^tEu=TG{$lc)Ot_-OjS{9Krs=V|-ifFStS@iQ^|cqloq~wAUa7tvN!IRe zbVfhN?Fi&-5o)tW)onD_(PiVL(MN=~9+NaD6uZSJ2h|=-(j&ys7cv-J~7>1cua<0!0GmIXZ0+o zzF7NbUG%~vhMbnB|FqzC&tgZ(_$dAz1rH0K#(?!IsYCt!s^H_!JCOO_&z^AVyXS3_ zs?=lj2PW>go$>UAA1}UTER)#1xt>?|FiS7pAUwM<Amlh}JMx^*O>uLc2G@ibXsZWX)l0(!8af!W2g` zsL3oS&P}auPcLu({4q1&Xzb8Ec}yA}FLW+M6{sl_6c*%DV9AB>9w=NIa@#GU!LH~S z;Sq(ry^lV3|3||{sdhsy@;r@{vrd@|wmNtGAivi&!|$1xS=Zhp&BflAa@Z! zLQoMF*!6Tkr1}p684w3`us!s6RT+xvEY&A$-aBFK>$p4`-%lOS}y%5y>{o`S>+MIbWT|bibPV59Zp5F`Af~X7$6#_U2F5xGCN3r6}P+mSziXE6OQ@ell_DCa@gANF4V@LUj7R2*(x)NFWa_ey_qH!yh? zYnJDi`iDm%$%qkXUeB}Fs)`pg6k4SAK&WLF3=rCw4&CzTOO1MMCV{x63V06>=csir z7rNFQaacD?R68B!^cDeUA7;Q#h>LJ(aAIfEna>I@CLhp?WqAs&3SR$)Hy5F zh2+*XpJKn5LK`DzO=<`SnO|L`PwK+~*I`vVp~~6-5FcKy$GP>MKS^7Et{nwti_A1(TMrx zssG=*b3o6+^@DOd4jO#bs{7Uo+?9-s!GVqet_V-_REy0s{mbx?)bL2{m@+&{vGLde z{S331f=EUs#_u57z+kWME^v-7t*|~^2IP?fBaM}jg4Y@d(pb>xSfOwMvQ`OjDQVQi zJ^zzt`q#7V34H=n#k8un`4zb_R?r>ZByP~yh{7X+0jz&OvJK%mW8O+%Qk3KeQq0=I z$oo$jLnyhUjR(@#N+5;L67QGSIvvU)!?K{&T`CN$z=^Qvs18hp)j%>DIN0JPE1k$p zvIr1YuyFw#q`HFFIG2E_$88W;^FTE_#Rr@6MeR$cCm^-q9Wyp5%g3KJ@1Mr9>1F^2 zi%4O2H?!I8T1;Zbk6b2`5_OknM{7)A^ce*V%djgd71E`UUNOk95~Tey(2L&(BHE}N zr5srhuV?@?*Cnp!?{^OlK7w4xTn4pz7ssaeSqNEpQmUXK@I{Yo5OI4F$Mu!@-1YOj zoq?Z5st~i+-`;%{sJa$G3c`IG81wdn||uk%j4N! zP2L#aEX~rdXG8=-$k@}|yhM22uUx7f=F{$iSuC&_%Uo>gF&iuR4%`jO`vJ$O6S(QN zb$36d;4x(Yq>`&b-ui#MD#JxO>E;MxQ~h^pPN86OzMkQQ6shF>1S?H$H2*dI9k*?r zL_glF7l|(kRt|H=kl}X|2gmnMi)X?Zi*$vWUp~H?R)D6(vA>dXJi7S2900NiU`DxQ zK8H;;EWpTZ-4VU1n{6q|yGSY1oIDWZNFu2;l4;Um)Fy-wedq@eAi;n*SfBn{%B)`1 zsXD!-Vs1a6CwYsOtobRttT&NU#d5Yj-?hwmMy$eWsz5jKNxcj$7!nV!sT67|Mc_%^ z-u_uf55>|Qw+zk&hG&crWOxN2MP^Oq;$EFwUDsc7yz7*I5_k7&K`xM2mpFY2tDKw{ z_ct8LQCeQMS^WXuxB&l$c?I*)``JDV_^W8BY3wAMH6?~!T|Xb&-vguT60;SS%UWP1 zzD4PH-X2C-SiO@Z$5DV3+{9ZtoNtX4^k0__GCux4Wb_wy^pDDSLGI{)MxF-T&0 zq}Je8hvjNEmanwK`wq%!^lWV)-CwcbrP}#^zdP9uFu%9kL?m(IG?O&HaHl+tCt}r_ zj?F7XVtEfWDrM4q0y8XXDtPvfwx^g7@-4ACV0c>mISgNiQ+EOF5Rv<;GIaQ&AQSk- zH{B;19TU5LegX^`lzMK`?|*}reI6UhvZp!C9OfO&vZ8stR}(k;#QO|416Tx=7!N#- zDACUfMGccy1zg<>jWJ7gF4Q#mZ+rLu?r6X~Th2%6TU6_{xTPl0;(SbRMWarxkw@b8VxGnD~{an#n+>X*~1F|W&HprugR5X*myVPJ~BiM zU)C4+iR!t4go|{nmq+^EZ#e;%R;~1X$X&T;&9sgH%h}fB6?r{e$vUv(IgN@^$wu7| z5nY4l5 z($^Lxa{VIhK3)A6|8evD`IBz5lAB8(V+s6HY&V)_v6s(7POMtG|HAy;pGl{{=`qw(~#=H8zCLjJ;%6ZBGJ%B)sS2{e{ zcJJeN4lpI5Z0Z&m1>TA#pX3|@o4ic{yKQ;Gt=_+bk}*}xLBj5Z;xX4$3kJR+;D2+N zkF${r7#ESC@Yya0^LVak0P9TdEXwG%vukkAvas52xf1lrjG~uYrWu{zeOv*HH6p(D!){nXW&{I=_;sy^p;(>e_K%f0dY474SRjGJ;79N zdg_2M>G2%SeTRxeiX?QS&01l0Suo^`0h#6 zR#=#Spc>Env4v{fVMlO)Z$IPL=8Du`29y)PXSc6nvb;&9~99vHf3IcdQ z7_ZUOs-pS3x@kU0L(%WRqTBA#44J_EA(oux|1L|BIVi+%SI-gKiZRX#)gSLp(> zUB8%O?x_zOBT0>46~9InQfkdUwZX!4QAhKN@0g1DT2JJlKdAv+?+pOR1nR-t=W0!7 zoJs9R0OI>*zr&*SppGgO=u$T#Ii+7D+Al0&YL7(0<1VW&z#Ipbmr11r;yOd5T< z+K_(ft7nUzo=vt$=Ae+PlYjSH+CTd>A1>4b2W%7P;Kh5Ma#0@ilDsRANs{0v>0tSw z$)SQX6TS2xvKyFs8iKH8n-hdCIQr>2PBJCHPT_ld2;~>04P{Rub0Ymy=b>3IflpHv zRlm1l#o2OLOqPACb$lJ`Z`uQfQ&B*{ZFVKG)HhXQRmkJ5 zG6hN_JI8bDxbxk9XU)ZkDKvLPW{vfwAK2){SZzq_~)`PI)7d}Sni4LIE0_hV($UK=85zL(HzIa}_mBogSUtgMr zb|MuL)WUAeb{oSZSZz|?z+JnuY;CAa2RquI+j3Y{+kNXY7u<-XXEBc4(dkQd$i_Ex z-2Z&%0Py(jO9-^wwT|K>$e*CJm?A&E*{+0c&eUl6{}E zNPbo;clxyKW(KJKnm|h?=!%5R(^Ddm;0vIRR?9avz z<`^~of_X0U32eoM=D7{U1Qw79M6?G1sR76meB7s9> z!G0a5^LY;6`qMP61id0V`1B9_8N7%eDE` zwQk4jJZi26`~rxWw4P0tna}~}*S(RM2{eZe@*`lRtF82$9oNXxzaMOVQPKJ8UD3T7 zT$(&l_V<1HlHa| zvaeudEZshUwq~)ZuLB`r62)jA+6qjnhg7sUe_{LkAl%B9ReMQ7!&g}i(dj22eZw}_lL-qs^E2v z>?zhSL!{NVW1TI^3TI}%=` z+uQ|Oc_jv}+MyDIUfRd+H^V^(y2clg#R9@ic{Um`meQt*biGs7P3vn<(THDJfm8v2 z{-}5sO7~Mw78c&Rj+d``tNZmO@L;2GPP6$ z_TJd!jj z_Y>toPvqu?vsW8RJ2^!uOf2nyA^6Zrr7Ig!3_t{ zi8Pjg|JojaRPf8y3x5049}jQ5<;kYoECY(cyC9PiBp$nvKIH%K+yByl<0q8t+WQdH zZeBMeDnXMV8$m0X0eHN(dCbS<^lGXI{`?%!LCji*U9nIq{V4Etr`!HiiJ`|~Q zmWMdhx}NjMKhmQ|J{|#wG!EgWX#m%!2j5&FbDDoS@>Nf6;7jaM;x>#R;zNGlMPOp{ zA<|$5AQC|9ffxiTOrLCa+oC^T7{O1_XvKYDgsdsSDGP7_y^lJG%)T5@1L{rbxwy?9 zK&hi=*K2*t6F&rwn-C#t=UO{ru;1W~E$4raqu5=P3y}9T1FhcRGX}dS3^sQPAbr^C z==E!)^cX}OFTZO7&(!S`NSM%vzZwjh5zJQUyQu%Pu6|#;B03=Po26Mp$0FSQOw{WL zWG!`pAmsJrw(;nHT_qQH=S+%q2uw|>P2wvo?%HopFF!$oxbgj-ySgJ?N}dC_=YKZg ze?^#)-)sNYqTWjt^WEcK)PDi4cwglfYaT!3G5}QlrL&L?uL|IvaPVn=?PzPt=H!Pi zzw2_D1W-8n*4}**X&eKfz6el$ba~#yIN1O7G{z9v9D51a0M7i!5K5k|(v|+ZpxND1 ziYP4WMQ-2#f;tld4$_f47V7-g7d{D(-Cx|k_v|j1oPP&}{jb%C^u6a_t-$@0Z}JF~ zkKYOa>b}7-Gc)t(=fy05b$5BapL^jqHfI(l>=k$ebf` z-uDT?)Pj?sn9F?b)c~lPoxuA)10cs>C%l#gP`cqpMgHVASUZCyl22W=kI%nN8UeT) zSvV6PfqN7jeCpBDzy=v+FccZ$$yJ_k8WA0*k}0A#P$(Eyv(mo&=SK`=}~CS8p5*EtiJQls6D7e%oIkwd$NE@`f5{2D9W?1)YxM2Q~p{#rQ21-(mc6XYAU<9y< z{l^R7EEvX1x!H7y0YqcO$rG6j6Yf{1(!P~otC#gc!(0MmD4WwR!|L~!adH~n*PZyJ zM5Fbt&d7<;`?|HA=c7t6R!z`$9qxl`>VWu@0f@<_1M>BX3U6r|ffGjMJccjbAC=|! zH=ZHuc?VWh;EDV3>~I~ZYZHhJg0xTUBRC@xf0ylgUR(Y#kWY2h$6UDJKXl9ucv|4buX)Wp_^c3JBI-R z`Xrpq6G*XI@{d9(CTMs=Ct+X2uH7yTyM`a!2C%yH^XVj#aNF)g_HTkWc>V@}dg{Dr zB=jZriXWQ-Y?hIDVa~@yENnP>^HCi_v{adJMWQK`CS3HeYLF5D*(;gQkhd_aLhSf7 zcgRs|sGNf;W&$`36EAxj z28A!qmD?P4U5cLt-&CY5*y)VtYgA^uY?Eb-ONHX?ncz`y$@t+>G6Kb_{kNc+ zW++Y@&ZCQA(KjHxS^}w0sPa4qH2l!G#|cK$Z^gK<0|27X^^HtmVzSIa|I+;0XdIzC zm}ZFGD~G{)!{@7*%VXdvG2-3lM(zfpqCl?+0H+e=$2^6)6ExjrP9=`&^7Z~?$pVfx z56Qd_fjRh7kj`UD+=7lz!3-v9(ZVVhCW*+Jb`4Crrkau2DqJq^AayzqRTbG;>v(!` zfSKOucFhi|A>s9L_cciIWh(*F%sZTqBva#95^GiuCtmM7a;Tq}YVpVaB65ZLGFSQG zxf*=i7W4v!yOC{Y^7m7`D}^mlC*|Z>NU1s;wkElI3n{4RZg|;f7g_!F0O5-_-qa0) zH$mM@eQcV~Ew&S?DJbmDk1P(y$$%NUWjhGX%!}tVM!*i25I9>?wm11_a9VH~1Z094 zv;9`4fd$SlWR{^EE*(umad55*kz3<)c5|?EFlNkST!K67ty|PQmgCv`Z-=Tb!hWrP z^PY;j1@vAAm4dMeK8(b0!lh3Sw?QeH=9XXgPiAoVo#x6NL8=}_SD-A+;GC-Ba!z)s~ zzZ8AM=B5=}tK zkko-l+Y(M`0I=!{3NCZlRHen383ZlPC-8O`xD)4}Qf56%q6JR-pZKg7vgN}c+gp+& zJJcfc(X!9WFV8KOtavCfBhYBEM{jD>IusjxkCVK`o(^y2P<+>}io1)u*cr>7-e`;E z=g$avO~vlw;=Glao2o2 z#=&gk^lOXUH=I#U(_sd3kPm!>k&0HmAQejSbqfr#^S64gbp@e4I1!kxumpP$==6x^ zAOZ-q_i5{A#2dxV!OUu635}b#)f?}C_<_w`H8yw8?G6Y5ZD(w;eujLi*d&pDlO1&$ z?q0trGe>RoJZ_cqm}M_Fug=^JlfkOMzS)y8sX*YPLeed2dPA|*58<~y3=EVBT`>4c zfTe|OZgjo@sP~*UCn`_tw~r$^lw?IAY&g{-Q)QNP5bpM4;?Uyz&*QR5%Zq12gf_z{ z1By(jm6$n*>8!rfy(>RYIf)4EXaa=KqeX z&arql_NHuYtKS^Q6KnS>2kLDb*7WHHD~+S?(qKY{cslw-hS1>nxn1C*)Gt~9Au)TDv~iZ z=VQ4msb==avQ(Y6gxKBRdA9!CQ_~8O*%(4;d4J0)XZkhk6zH?~=so1$Er~vMT+aXv znFLSXYzjYdaqH59s1ZV@=W$#VB!NFVk=YqY{8)UtM_;{i`)3{#8Q0rzMQClsYA7(Kc z6+&fe%9Qbl%r#aUPW>DV6RXKj%Zfs(wx1^nsNvla%2?!%_FRdg13u>oSQ373Qqp;1 z{dR&SFbThVdSkp3&Ug`}4nS`M3%rP1vvlY9n=y$s?{l(!FWx{WfkjEq*4%S$;HE(Q z9G(QnAtTNKIe`y1c=KZDq{ED7-}EIZ6t_=Sui;{;ioBx)ISk?hy~B#LVr1=ZzGRU5 z9icQ)AZ0X19;bMG1}tZ)+3^C`^W*PFccu)lyiUFJry?vSzDY8wd}Rhn$773kRkk(v zUY#fB&Ahpq=-`_n1%wGl)cK}2Jm>6UI+o)psl$cZ+8u$#Zigc(gJn-%U9uD?)kJ&n z3drK#w1xP@`s-s>Ult`7?Gz=BZ;$4y=VGLTT~9v+0OET?azeBmq1FkDosLy5r#7#7 zse3%}&)Sc9|Bg~!JZ-ec$#i#yWE>4;o=#7njl9MxBwANMg=5Pd?B1nGy5To3$XMQF zhB9N0kZtV6aFbb->N1y!)~c1T#ufFdG9sw~v$GuDAJA`{cK9K*nOmOhHw2 zicHW1AvP}Dr7=x1#OUzHAZGOS4~(3^=C}DK0M^GMfxPbEh9rsMQw#k#bx_rDP}4<4 zV|H#S*BfY-WQ^3IMuTat$bMwj9U>YF9<$QgcmzYtpLuOiF$z)oA)E!?vW*6s=>yTb z;ziT@7T{8!Rmgi?_WglOidUH(ChuMA^5+#DHN{~oYbU=FZS^=Ep0)BK|t5{Nx7`Z*-$5ai2^wrzz` z_IOCcYA=g~tv9Rp(s+)2E5;ji!WPMVnKZW+o$Xyi;dzAT1RZyRyvQ`!?kpPzN8NvM z`>l7l`O=v;6kUNB#~Me3tesy0q&sb4EZjM|DKHO1ysmM24uF@e6&ebpM^xRf(L#+v zG2YBmI~N8*Pn*+aZXE+ z^SE?`8?Q zI5S(cAqo88Dub3ir zl2d~|kJ+pv62wS0_AVd>s*Xj%%)q3QH|*-`Ps5qn|6`-Pm^Fl*{2VR$n*T8D#fz9e z`nwax-tt;x%aqMi&9|@(mxDjA_iiS85of(f8iXoCUKxq2qdAyTG{cuc>k;c$B;(Md zdrb}vw7!O1+l`AolTbI!#E{G$$!bRKB_{F+LM z#1qi~hc8{9h6zXbW~m;wnEb80n+x!uPg)xK9k-lG_E2EHkp{&OBG`8Y#fWucvnLgx3Q6{uQ7TU7m@s1L+H=&eH#G6 zF&>b^6WTdqX$1YkU?$IOkAe)2JJT_&c)d$YE4H5jY$@cTcoYADMrNKLFRdZUAPS7z zgL5BeagiBspwk>$sFNx<>&W5`+Ve<28$?F)D4y5EjocilO-Mgh#Xx)M@FI?emVAS4 z$9dmc|G;-kB;amyE~tzdfe9K^u|8j=A!(rX3~QITx(@_+!U3Fio8FRYLja<&y}1Yw zEz?-WTDlL!teK~yd5Nksd=zw{g=vx>OojMuHTInYa-k{5n=|Qctc}f`vn1TIio|aT zj3~y@@l}1|)4KRV@fC5}`8h$TbE6h961o2`;&{@E>A;7zc-mU|IsUiQKv6NB?&XIR zqcLC(V|I=BD3(-q3!Bt7~OkxNDwKs!-K|tW>r??pymjIv*Fpi7ekeZRC zGZY6dM%=)M_N908Tk0md+o+HBxB)j&t0lu*@AYQXU$D)&p?f0pTSGr9r#ZkucE62~ zo&Ta621%Xcu6L7?v!6oDcNy| zUWzz;jX1|rdJbusne^ap3Jr%t08Cd{>^?z>|9TAM6W8s= z7CQ5`WTrB&`^~rKANWyeN?ma%2KcRdxkPsoV$54!U7VJ@ofpHkYQutW!_bH%@4_Y< z&#`n0+ zN^CLL#hM*H1}Eb}WUUe{Y+62Ev@uBvbtAF$R9sd&6Lqhu!@F<6)aEIm$l`p5( zd1#3=Mf03$2WTe=Ca{6q~TBW@At zvk8c?o}UOrrC>my!6W}Z z6u*Mc5wb2O-}`+MAf$lW*@8Zh1t1Xh>f6-no%aHlcJ!sf6y|e)9hlQiJT&0&phAGf zdkLKi56geG@;AG1)!!#KdB?(AQ?>IRulXt)ZkB`wQK@Lb zH!!FSP~dyhcw=gTN3}}(+AYo*=d=@PyPL0G{!;g*3J9JQtUUWF&jCG$)SE8`J zmSkr zS5@qVqL2p9am482Jh3h(bqNk7O%G!&yTk? zrsi??vO;KtwK`T4Cp3IJB{TJ>rpd&B{UY`k0Ma`3%_lth$QMBp%7&fGmo#VXN9AOs zK-j?*^Z*`A>NwLVb1eR=%jqg^?AN+07HO#!(;V8(`-HShotU5 zL%9Yp$aO<#P`_{3XFynCm;-kW#2O)+fL{1e7Xt58RpV!+m3FQJk)poGu}YcofcZE7 zP89Z5h;28JiKTFG>=`vtVv3Vl<&HvSU%lu6&*qckn(=u9Gn+YjRoIvf(C4 z&retJp%PnqJKB(LxS8cyPT*%E+<(Q{Dn!II?yl3IR3t%p_^l*>4YVEhX3SS=S&le- zJdJns0AZ4k4ULO#C23$3oQceMZ3TlHm*;Inm-@^{9bcNSq2*q>>m^BX_yRcjbLXN{!`g)9A zO7Gt;H5zX~II9fvOx!>Y!u!xRC3%C1lQjE!MxOIaFi7k9Rp*#gY)+uL`;Fwh?snMN zBNm3ue_4*Hrl5(Ij6k)=A{H5bYv|T%U(UQto|dV-rP*t*@=lxv>R42Ge!Rg7&^Sfi z4KR-3aQLC~P)iAh4c!Mjti76DP~1zw@*h4vA?(X$gdTjBU?#gj~rB6p!$E?02rm^P=39EW?HqX9j{ zcHvu|V`rmoe90SWQw#S#7y>jt?#CsG$Eci*C#kX}oQ+{+RqE zt4Mbm{m%;Fzoo+O-<-q(S9H@e_Fdbbe|v?$yjn6NieH>tj&t;sR0mWsCDU|kAy7|{(V*q=F-tr z$iKr{p)#P1pyIc=+Zr5m6G@=@{mZjIlfet_q_>Opd#M2SaDq_w^;G~PTTa5$p%EQde_S{ZZ%}H;_(U<5nQgxv;=yc zNLop(tSzD8;J*y(9~I!2YyEvx&yV60(~O*)U!3eF@?MD8033UWk;e#O2)qkHa;x9GQf6npmCo%yH)$cqiD|MtXJOi8r z17w||e~vc+LA#UvU`qrDw$|i1 zwgV+56_9w_k7RiuV~~*^pv-wM*j@Snas z!=)Pu`DeVI8V*(OFU#)UZo3DD^|B>^&FJ-Gh~%Mq06*#Lg6ZlEu<1``0=E3iA`#Kg zoX(+T7Gug;f^HY8NXvh{_TaC{bkc#$h>vC7jRiU+VZDy4^QB?=>iK7nL6-j>o9(7{U^FrEil5730%0Bp|)={SY@rB zh|enI&~tgR7*^tT>4cP!snuBK1JZ@WivF~q(*e|6`0sGppUVzra2p{JtN*sB^i7{> z_Adad=CynUDwN<0iwqJq2Iah1JEw)#!2BI}r=znob`S>{30w~?ch%1Xa1;n3#xbm~ zqlZmg#9oF}w3`7IA8=~qX_ww$LVN6!+l(9x0rA8qr1R99iP+Gd+LZl;Bo@m&W2dUoBMEKRRQ5*(yUZ zX)26Qo1_~?EjU_ut#o&Hcb7CsrwXFdA>G{|U5i#gy2B-Kp7nkIz0cX-*?Y!u zbewU3^}f$@$M3qX8k(;EB|)nOTtei!1Tg^L-$t0umS~P!$O7skRf*#HAef#r7R|AAG4Q#?-S(Xa(AV-`~1AZ}ZJ{7AlFfeF>0%+zFu$r3{@i>G&wSN7%D&TTF z8vxY1b%8fO(>2%y?^(v{{^NxG_a!EV*!eC<#))`#SG@$TXFLVNcbjW)beSeW&fQvX?idy~gr|~RA&k>;t?rZ!>2C> zq)%VdXRj+~a!D~NWKe^{bqeYTI3b+}okgnDeqyQ^k98^y?_q9niMn1#ol5{w?tJ^; zl(fz7<~0Zew=O#+%jxT{`j2}e=*7RZBj4t81;^|BpTqawU~Nmk_7*eIzbfdxp0Uy8 zLHZ7|W8-@+G@pR7)%LwUuTYnfI|`ho|3AO@_kWI*6C@-1=B{M!3)_>IYa^4N)2RA|6iJffZUiVp0)OkF@M-dC-v zk2Rytw!guR`m==AaFEE*Z(YOJlQej9HSm_zdg@auU>Fu+33_$XZrc=&oe+>UMbapQ z++^7d8lIRBwUt20ADfDQxjQlKXK>G&Zfl85l3oMa+#z(DvCsE{2X1J%bW)Fn#eDwk zR88>>43jV_5bNjjVK9(RCbWDUK;IouY1uygG%SQkIW}SXa!$BVU}YbBrnW+_krtFd zX-|?0WRnZ!=)nZ_a`gNxbIS7DE6>;rPI`bTHC0sY$L;Zx*Y)>!oovbhLOEybKF1=78^d z{n6~Lk)ViWb_;0VM>n(SWFAE-f~tT%&sZ24M|+vK60SW1((WJ37ieuV4tKt9xGaXg zEHC6-fq@iDgHlET?sxcFJXz4y!ZUKs^7;AOE@Su_-ZnKK(=P-YvD#6T0PE1MIk4O| zA0xeE@liw;3ZdL3prej~4noo<;}^`1CmocNXbVVAq42@T4ecP&>BbFar=tAw(cUW{ zl~DgPU*kLOi|N>4-5aN4gy9I8gL7kW?beY^fF<{=3=Y+=I2SM(6)8SOnZW)ip@QjH z@3>6(xaikxSvJTKgl+}(jB;$PvJ4X1j(;mxEni?~~K;#~{t1SIPIt5sEgN$t?pfXetca zC;bU%{E)t?7DW6QO`qG*QOXr&z-LmNel`brQ$NC!vy zd=!?O+~rqV{OLe`N-CTvG$gy96rtILw)yLVYu1_o9fR;0@yBp}HMVhpD$&dCGzTRU z6lObqCkm@((XRPog`oi_Z>oO|>G&RxH~JoTh-W^xEbjpk-{4HMIOv|q{va8#E7He@ zOq9*#9j)ko@emcq>g4ldgQxUBcO5LEAOpQ^#*^`1Yw6~l%RJ_DomD`mVrwxVgX*s? zxLRI7sZN&A3?*P-FJs>Q8=K2*UgcoSZ$+MPS%id&_!gF*?sJ}h_(4F;JNcYlnj{aV64zc=$-rwaGwH3OG z7p6|zK2VxaC!&ja!Lv>=2!C;3LsjyG+=tTV)Tk@?RGPX+{o&9TxQK!$SGT_prhjcu z3Alx6^VfRUY{84T8=xpIn;LPPE!Ss`A!4o5MmfViss#p(J9I_bEz=$cQ1H1ZsUZMm z{GTm{^A`?5%^rKjM>Jck`qgSG9jGSKpji9~CmX?Hm|&sUUw8Cb^)2vv;4=P$)F^lg za$)9_(pQ1uUyg{s&hPVsf)yvPo$!l(pr)wrozDYghFrqoi$d?1Exws1e{41+MX@S6 zv=$KsK$>vMmz$PI%R?}vIu{B)B9lDE znMCj_%7uBZqHMV{P!BwGy}eutxE?U}*D@nqLFAHV`~0U*9FH%4cg@3(m?K{d?DRWE`HwSzg3oT1<`|8e?03&Ri2M1FY8mIxmpAVG+Na_eEsfbY}8n9eF^`&76x4XZ7ea*9lh~u8_rgsjP zgLyPr^xglpEdy0v0WDYhTPGv zsC`dcH*uvBNBDY+fY?wteDz!0&-C$y|N!W3=JcJsQ!1+3OD+(kOO=b`ME zMCIKAm*n~-xw)(JtJe<$bM6I{Mpor7H~YE~zVT!2A={pDU2fMjvToS}?cBJbsgMB$ z#a_l?Df6Wp+S4W6_uRC`Bt&V6~ zH`mr}v~_v8EqF*lr{{fsopfm&sMqYO-~HRkzT4ewFRg61@vZgWMmfO=G96QY3f4BU zLv7(@2Er8@^*0D54m_|7h*oUMc`W5OwjAKYo^I4Gat92TpF<32FTyAUoWx$dgBypN zIsm=(u3V&h1~&iKhbUOzoixteTJFL7J(In?q!nik8357~U~Sq?|b zzMeDqg}~;2w{v@}X}_LXz%Sy#wu4eDhjI6gBGKZe#SUmf;LKrUcJT3n*AD&DQmJ*M%VgZS+#Cl0e z&Oh+!;!m$DHbQSzOp7(RfYJa7qFawD!Rm70z6+K6%&J}e67<*w!;)GZRZ~@OWC(Bo zEyjwrGy+hm2!+qGNfObIS!sdJYr-9jZuFMazo>{~DXY6-RAKP9F8jL8xvv$ueD4tV z!75<21I%qTu)L#SMrr^!((X-^NWI<4$6M28=XhS=sge%n69v%$@TO_A-(PrRD4o6H zY~+5q(O(Am_J06AMO|gmUwwN2>vO(aPGI@# z(Gb^$*nQV}QW3D7(LHy&OSB5~L-CWF6H80F*`b^-XV>W>_PnabQE(_rSD6KrjOQZ- z>MQRdTQ+SPo4@z=|Ex5+eh$cQ^*__NVy7%xyYjCv^pOKkeXZkJAh0%?bXox=^PX8O z^0qiEe{s($g4$uj;AkA!4}UKzjUjXX>jd6KiP@(gs|I!RrS#OM9X!;YjaP);+opBK_1W zJ6ZV}0XB4a1+x0PH``1h7a!Vj{ata9AF#$fezL@-Z(>U-2$KeX~j z1@hSJnfy&C+LI?C19*w8=~4Tw_4{%A*x_lX?c1!LMb?u=pIBLi^NSjx{%4-)VZNu2 zWH{er{9vaB*-KNKJMd5%L}x5QH^>TgmiTcfvz!%XDZUdv-`(dEL}qh83)2@*qDWAa z3iM$fze)KE46*+VMJI*vrasetwK?+v(V?8WiTV)V!HI^xgJSZrq){(&45ke727{hC zuy81kbe@9EEw)c@U_K8a9aasTk0cZJy<&xWMR;}+2bSNwSzCPT<=aW1MZ0c6z+biC zYF2MD)cJE-yR&LolWos9%CR!z;Sm9|#+Rrt0UwC1gf6w)`eN8l(8ZchpSuksWsnha z8yt^>Yr{9E#f?IaL-t&b-*>X%V8;xa%cp-WVR7(N1<{p-U4~!=$E!4(ZW!>Cgxh@D zbc=VfM3ep3{D%S_#}&ObK0CwXJMAX3uhC`@OwXf*gZn$q(hP-l>^-;h6(E$&(<$g| zLh8z}mwP7^cait-tT%>G9_+VDuj1_WVFyeJ`2#@+4^x;|FhkoY3;8>|MEPx?veJJX z!;H!xV@92*A1DU(sChniWT80UJLP7~MIJPA^>f=-;>bhIm>B z-EJJxVl5PL{4~wVj*zHDK%&C$_dbdk61t+c3Yw5?WOR#&z~@lErvUj-DW`buu7+~0 zx3^l+9w6PxO3=H|bYRLMw0Ss0ycL#n5xfa0I~EMxTl^7txZfyNMNNZ6m-O~&UDjNV zptMy!jn?`ZSz7${1akfnBwE3FBsGBZDyPwD#-s=CAcA)+u}Ts9EoLA*jLg^2pNd!H0w0c1?AT z9#7oe%e^J#cybXF|HyfRJ@E?yXjqPa{#x_&#P%zdd{tn&Gq+B?M&}oFLlJQmTyylT zHn5yPD-WBJYra~c&%5I)wcx~l?(A8l=&ko zyFQx6_*X1CwFx@$YPK71oYoI=@NTZDqpuYqhdjIsj&86Py+5zPco6?Pyu&P7&Eg{x z^nJ$x(jg1zMXR#bi)Kq>a-9gmF0hgC&Dh=4IEbS3uTg~^94>8)wRgf# z6O^}*8P#8m%tks#wnf#g7leN6q(yJHl2qt^$nSZr7pY=NsN4CFkXuU>gQd`D86~)b zrIOS|g9GO*0{N^{@n}>RVHuVa_p?;_SaO7IDUvR!R~wdi82Qr(fV0R|8*5ohzznpO z)@2&!+QVv9e6iM^skLqA)5fgj3z0W_vTxq=E>lv}MoN+3ab&18L_gSDNY56 zpqdenNS;B|C3O=EAI=eClw(34cZCeK5E^}b$jpj{UimoG9P*O?ag;O-F5V+@;rE2* z4Q(TL{(k<`pD@5jo_*VI=kD4)T6X}GMRhwxo&5yC_%4b8MKK=45ZUs@IDFlpB=LH` zLNbR*m%r)#Z=bnJHJ(OVCDNBZusx_(l=Y+%ZkY?@iOTA3k>(8(3 zokM0=1JpRiaU3#ED7lw4oS|;=5gg$P*_Y+D7VVeg0#r_}p9+g?3g75`=ztRo`&V=;w(FqbYS$hg^zWj+`cf*2kL zh<9E+7$Y%}I|OX#6-^HUlDeLuMOC}Q=-Ey~DzoEIaannlBg+=$cMOU} zddGqfRPp8*@U${|FLiK=*=qt+#>cz_Z1AToQ?#k%o{t=H>|^de$KEanLc3Ymql<$; z`YsWfpYDO>>#)Jc%N(MAmik-L$Vby*e@}M0@*n6ccIR`-fUX1$6|tdeAoY2=$c91; zoo<~&6_nF1GSv4`X|wheL?g4>4pJj8G$p`?aemhP87`wX0F8&4L+Zy;8wo7{ei&+9_i$ zw5F<3+Ds!{-ni=}x_T+s9xWevL+A2iiC}*O(+1z4{-TaSk_~cF8q>5=F;;lUS~4_@ zdR7>EhNFlR*n3q3O3sQMsSpX4G50ZY!O>ls0O@-p3*TI0`yyQ`mNBD2eh^?=8)7}v zl-eZQ$OS1yIJpZ(L(sI;GHlDg@+VkN^g}d}P~jvGC3`qW6Yaf>s=0b8FvfW;Xd83& zpaszmFgWN`jw%vGY0zi-q)9yH@JK4NMsYZ!LorQLX8#D^Sl&rn zcm`7N3J7{Q0<}b4bLqj_ti8HlBW@PwKFsM{=!dAk@8fO2)`F^#&o+04e%y8t86NV?vyxg`%`Ur0ry_BQFtkt|P`mgf^lUsXc*UkkFe3*7f5zxF_I3|Ll4 zh(EaZ>KP@Zw2;erQq_0MX_!4;xh3|8u%|2-#)PQc9fFSB?$(WgvXS+aGUH&x5{=%; zG<=54fdwMFsyY#9MAjbf+jsg+PuTdw!(o&^Hji*O)Q6iR8m2y&qPp9|0;aE?b=6ue zNa&lsW0bdvBTII!bTA8ZuQk%6uNAX<3e6eIem?Hf=vJFBr_Zjs@e+ZBG9){p%stFr zKT6MOsS{hYly+aL1?UI|a+f^e)`N>!b7f`Wf_2W_j_eWh@wmwc@L75-!k&~2XD&7Q zhK{EOziNYV!1Y8VsU_rOm71&J^|67t0(;L_iBh4; zPG1Z=W4XwK0Uam*HCN=S3h2bp>6Pk2l9%3@g1MY^Zg}JaGV>-TF0<( zNsioglZZMuK!n1ci>SDIY!oMX91bRT! z?Z$g7nkMiJGKf37mhIS{1d=}mS}!STdquwWd!hvRi3_VXm~BRq>JeT0H2}|-jFZ)z z3*$qa!(01V*9*Z2U#!|hca2r>EaX3os@mRn?NALI`|YB3rGANgMe_5@5%~O_^7irR z1$Mg^+U9N=`5ae-cZg z=G1#Kf>R%2^dO$2(sVDx+N*1vQ6;Re>qHO4| zOE0xtQe%iAMG(-=cS`MDi}v#_$Y^oA406>gsT2qu=Ei_d8DWPZ?(yVU=1A(1Ien@j z(C1V}6B4+GpwrN2&f-}ag6#ysVd4lv|49c7y(+gd?)`~ZgcZuL@>Db$;U`&o1W&tC zGQz@3WWs_kH9i+M@?sG&0$S@4oKN`Q#D*fFYrU(v`h4qUd_(HmZ#f9;lg?Yi6y8D# zJE=?ciq>f@0^--mbGbLhfMp%RvnhQ3n=HrAzfSffP{KQd@vih4BTZIAjj9 zJQA#Xv=l>pL^ok5C#a)GbbhmI{xOA5;$Nxw3l<;kipb{GwI^N`1EOrU>vnUeMrL0<&>nJG5hzL+#Tz>4}AsR;W zfeae47H-$qaAhk|1y$g+s=mm$=9DQW~kt7LuZ*Q_T z#zp2e{$s7A&PgOfWn4^U*JL;>iUg2BWy8f(V$rJDJ<*fGVwr_W9`R= z)_={?dx89+=|1QV0a{}Za-__Ck1OSVt+iYgZr;)gkZahmahZFGK$mF z-bUR@C-KCP>V*}*M}MDU@U<(~<%rhJ#u@v0|5JLdV9cklKmpYdOqYUySMr`x1l4FE zdfxrtkEUfl^O#5h`9+LK$Zw_Y3v_YZ``HemlSYf^Z)1J+R7~IUZOvonYp&}D`*THS z>A7hX6IH*OFpRcf1V29OYfm`78It%55<32k_L4i`7gw8d#?1LeE@Hd_Vp9GC5xO;{ z@N0QU3axx_I6_$cdyFjy47V{PjwuY(d$G6?m2VH+odDqwdRm|sY;zGyI-X^_uy~iZ4XWmdr?Wcb&vxpHVG|L zWi+JD3+ck3sPje$|MTF}9>@|C^oIE@V$TVDg;wo!6L+~{x z_!`KpE$56ue#I5W3QA-b6(JPZ|Dv*h27wAQF_i9`}F&qqt-yPfG09cFm%t^)Upj05BTE-g8)>`s*tYdxHKLSU^>KBgfYh4js_FQ^c@nR~%&xkL zZKl_H7qX!L?{D2K@d!d|nE&NJgp)YIbJ86Y8hYy_OU&Px$$-@Ze2E17VW{CsHh@9N zf%($U)~l78CcopgU@%&h1h!mkU<#eO=x!qq)Y2CMTsI>bB9frnD6$9sp7g-ru6FU` zJ)iy302urJ0FKrno4}Mb8K^u4!6f_}U{~zbt|bb<))Ax6cBQ595*PZ%sI?BHfG@Oo z*UZWkUjV3Pht8AoUT_y-6DOXg;dpiLE)CAN91f?m4FmQ=`1=|%7AvH8fz9ZL$*$si=a(&K5q2TX0FKY{(Z z=b>8PigF8s;ho>)7gaj_0{`d#;}Z-1BGDo1vq|`LJKD=gF^ew=n6}A%2nqi&uueC* zQy?93{`1Mz;mSLJM8j>?3ttZ6B?G&1xT`94lxS(c&Fq)2A2oqzj~a`FU)|Go#fiG> z&^wxs%MIR=uRklXf(Zfx0G4KMtz3cYtTnWu>N4^-+@TA5kKgExywe%k?X6YZk_Hgp z!Q-?2u9iKyOnT57T`r;*!Y^WbPyk_>?jBfCb6KX>_!{hFizM^_P+Uxs^$ zViK|>I^=uQ#4CfL_F}y)J32l?3P`zydy{;Uz94luT6s~niQ!nnZMwM@9J*-ODS@o+ z2145_`l^SM8DfAVmsD?i?sJbtzIDF;c+dY$c)S!u%$nh8F-dQI2FhQX0j7MvkIN}6 z>aU-vm(oMbjLI&CdkpEbPN-N~6Eg`u0rThIe}Ekw22w9byV zBKk{*=~ifP!|7^FU%WaRmrlltA}SfakKY8kzjVg9>;bTS{{*l%AM(I9%JW)tdsLBI z*u`8`A_2k2bxp%S2B7Ji`VFFFUvs(vp*GxQ8!btVu@`I{;IALNtdb>ctvK;AJa$&Zwuk-lx+h;r*h{@`&;+E_cNFO?86K zL8l8~I{ir>DlroWzAk!25+nu8a1gb6KI9$f0DQV!vGX@DQyaoA9Yy5VM0+QI`ozm4 zoWJy12S|jvbVDPsM-Hb=a6|L}t~9ee{mtgvGpEr1Jd*zfwSA;U@PsL3CHZ5s_ZU9$ zyFUKV6Mev*yf9AEKjO2+*%Kt$$ti<8>v}z27shy*E6B#uV>4IGTDf(4a`Kn=PQ%Hj zT{BK4HZyk*9}M`j^uoT@dwF|*{Ym!h6WBhTZjCa_)*bIJa1W+3ryq868d4cPO>7V9 zC?2otz@E{53jmKq=i3o?$rN7i1x#Rz%Wlx}MY3V&xp{O-h?Wb`B~M@b6mk5%gB=Z5 z{~R_0`h(qg*a83QP{q4I^?D6U+qM4z{YLF0K^2l1s+Fe$0xE-QY7J^$crU<^%Yj@V~6+m;@dbpsHOVCqNqujPu>HsLui$uD-wb+ekM=dv}L&~D} zg(1twj*+&yXE=SoK=3K!R0s4v<2KSe>i^jPe^Pc32K0tZ4H;GY%h4?UH@G&Pe~@>5 z-r8l$8Uz($Mu~fXR7jHN9;J-<04M4(WPAk>G=?`t3b@s3^yFfMD)e?8!r-gGm&N*) zkG9d$DfB2)OOgLg%_FE{RUaS)t%U_#MY&oX78~R?r#eX=9e$Gy2pTE1SZKAa|H&m} zzsR#t?BGBd$c5D1(tw|%`1JP(oVbHZUDerZG;1+R~~497>;1x<^W1QU1>hj9yONZ2O$E3 z`RwhnBrl{vPy)UC-b`&gYJG6wk|QVBw7ypU7lfNq3@mbcBs{;bke43tk(`97r<8Kj!Zqh z!~35QUC>MJo@aD^nm-gP*MW$JXBuq=rHq@!@txHTT5blr*N+hK15t~3<@=$)rq4}A$%a43bp-_W2? z$p4@X8V3Z)3W(LvkD;5}zsm+9Ec;JjZ~YAAzW!>aUZX;X6&_}+_VrDxkEJw@@p68F z9L;9s=g7}kq{0Ie>tZlk6Qy93+67}#5hQ;4IPuFJpNm`WXG?0^7R0!f+mzDFaBo@o znjbIfF%odyELaZ+iE*ZQ$~Y_suBetOBL*3d5YeG+ofwphe_iE+%QOO?GIZTHwZ@+Yf@-knM`uv0A&j)f|e(xsE1x+ z6rk%yfT!S}2NWwU&Trs|`+!9b=exiVNDX53@dh!=bs{l6_yD$OI0Fg7)pvkv3WNMC z3*ItolU{p(K4cZ5p@ojms5Y#ig^>5$Q|Su0iYY*Fa1Y828lM5l>#0G+J+xIhU#QIC zctFMPe#JlXR{Nb-WAM#~WwmBlh)iDdi#fh8 z2_fV8z({Csas8Bn-$AcaC?pGiG4F+|Mp<+XAk5x+UzS!|3>V*q9sx=bX|`fu%nsHa zMQ4#Q*XnyUoi%9|>;L4C6Y3tY7JoMq#&#zoHBLA7;@Wmq+HO3s;+7e5u8^z2ox*ldAZ-e`{1n#@-e$b!r=t;w2xnVQguT@3%O1r*07VYYtdEoyvv1zby zr&}{fahoIT{n|9q$NUPzt+gxpyp%ItE3e9pZ7YF(%c~(x>5r>~!%EZFIg)a^rHy^^ z%+1KHmZi&a`~NK0gTLcPZSC#By1c^LQ0sso%4-u{Y&wdiyhxUV3rP5a(U7W;(hB{5 zU#f~@l@|%RY^el4ButT2dtIZU_!UG^=h5}N$QE$^7|oSHDViKZ#5N>DSFZ+qn**UwsAo=qu>zLinHv0g5!odt#`?JdW?A8z#UU2nL?3LX0hK9w3QA7dMCEs{@&H$jHk13 z->)N?lLP3l}%pn$D=PVlk4tOoe=0Uwv6oEj$ z5S$Yhnh1Hg_pZR#IGNj0DGu9ba|`=sx1>T%KuQ$s1SWaK3d5qVtm~Ci$$DxgRug7j z(bw_MnAMw7{`g)P?%f9W2t7o3ln9K(nLW`81QMK_8~wNIpl$BDBpZJUSOCeVQDMLe z`H}b-?WiW9*{?LDT97jSMZme1{w<(XO3%dolvCc1;g-Mqp5u9t@vq8l4V&8NPDNI8 zihs?_1)Uxej^6ObEvCyd;SE!8ec}VX&B;&7Ou0gylQWW!!-&n(c)m`dL0ME2mx*ZM z%WXBIn~OQaPY%mXY2WMompn4h_vi=n=$bKDtU_5jfe+7{b) zeo3=)4=ldSjuSi6F!{s2u3@lD1A!317g3l8d{xa{AD=6Yvd)#pqy1u8&@AwrBWsk!Q=Me0Z7plEu)XFqw)^m)E!$-Qi+ejFT!eW( zuZubNwT<&2;xs}Z#Hep=4-7+Crfu)__7X{;Q@Nbt5~Po#N;CMm)abgo{|cm9K0m@h zs$+;rAD9WO81h{8|EytkZLkLtX~+frUWRMgAD`lsqS44+P<>y0WfJNd8JM`|-MSHI zfQy6A$tnYO`6D2eM@rc(*dKsSZ{%O0xdGGP6tIbt-(-xNImLc!!sh^ef=$Zv9k+t` zR@^TvDAM*~$+1nn{NVXF%@}EVXz&hdjC7QD`}nnf2MKiaCEU@ zfkhF+6BCgE3#_V9mMH(NVi3yi#2@Ar9`cgMX{p71FRjhve7mFb;ci4sZI=)h4wWHQ zEhG2dQcahzq6Z|CIUrlv=JbC62KWO=nA6+HQ zT09v7w5Jl@1&XOrKQKnh*WJ4}s7R9>;KLZ^XgFIS;-2CZ*<3?JOP6w`+TCymr22Wr z!mO(37Uvm>KaGfv*W;+rO26n|xK1x1jz-4jX&Ib|l>ArCzp)+#u>Z-?h$J9}>TUMJ z*aHk!uFi_(D;5Uw2)5e%7F-CpoUw9%jk>bzN%6?5lqWQpTkI;#>@7juHUk;bfXoI}y?4GvqjOO_)OxSo|ksqYIA<0qhZH4U+cl=^=DIgyTH zBVt;kAB=<>9xf5SDeUHq^0`Fb%v@b4&!Lk!2o*ZIytw(x`uhKE;MQi)I2yZ3PE2j# z!yB;&X&PP~wax&QsRrRrwPKhy&nkODj6$_6EV_`nb*zzRRxXKm>DJl{Lc$(E zUIO^WL;RbN^F~d+B@6sa~|B68blmK28sZnp& zZS;-74@kWusR)U+BobsS%TAQQoxjix1CpM4I!L9VsQY%-yGDwR+PdsfHyzrW7Ec&!j&F)Sn~voX+1(krI%%+pBv#P2_M>+uKeGMDR?!f^3e8@Ay;O75`Opy%QBi3p~k zpa`Cme~`(wJ(5}A;8n@OtXdrgnpNcIPtkPrn>>=N-+Ug_eAqtuyOJ7K+HZ+qQoG<8 z<^dnv0WFh$zf|8KA~MF8FWep@S_DswK1A$)eNs6Tf=r2rDOMT!Jv36A{37xPLft2W zTc1zd>0uH4C7tC#t7w%E$wTdg9JQEXF%7 zRn{hsZHaKq!3jE<+5nZ2oH$2O2nzumVRhcS{*Fq(GU1TS$`*Mp;5>uOU-JR}@2wsNV@?$9arVJ<&;%Y(t<4UL5m zx4R6CS8U26>p*cjy@xZ%1Id3x&JkHOn$cEKXKITz zzSJE$8FyGB!j*{DQAY3&!>b-D(9eXB^u#6-_Uv`h(RU%Tyr&+UPl)uU77%@jwS*}D zOzZK&9RHrc-nekQoj7`zpBcz_X%B_6ofK8GMGWt3NwsQ#zbD_RuDfoXOvE0y-Pc=&NWCDU-ctd zpF7{@wONzrmgnoGR5f|r7M-=+OQ;v*DQlLikF@1;Q!BS9p^G8U5AE`{?hK~y-oeQ$pgQ8 z`Jy+3*OxpkTV7d-iDAacd$3B?$!&uDpLf*XEnzWI*YXTcamaeh|JER@qlF1mtd+!G z64u5Y5-KM5yS~6Tj@@y<8|Rizp-y3QCLhcZ4oJyhW|I5ws``o;u|nLHEe{sfe$=@n zzh8KfQBrz1SCAQ;gdxa+NpBS8`@Mw&`N8&8e`?;d7JQ@j8lvYg4enk36uYPRTIweK zZbav|&lujEdR14tYUd7Db=LlzIyGQ(zf$e=G`Bq}cG-mCn6Czra#b56$U&Hk>hy1{ zy#+bH#W%EXKywu7S!V55Jrz9pJ464bS^hOx`}bu<8y&O+{JLmuN~!zh@}H!cDUukP zGy;OAmN`?K^3S~1$Ho=<0l~1#wQsWwX6p4pnR~+66pzkMomBVah?yh28f5ChFaeq#Fzt4YB=vWJlf8_l40tehN|M@En6X9xrwk6!c z{bq^*OR;*@dO8rjq z>du>CX0x2pOrZ~ETu3eJ@>jB}$iDPNOPC~6g$=^Kmnt(U8~WIMH4`3G6S=vWPZE%sdsAIrE-`~KN^94go0QQ(hgn!1ZGDUw{DWZyev zWXN=!O8jPBbkxfEl-zVPN#ZI>tE;46UmVL>I{vyRfkBbcKAC-waHc9-d4;xiRhgM8 zeemOj4~d2j>!I)PqN(O59Wm@TTMb`2bx3ZQtZ{iXc&kL)c(?X;WYtyN;y{>L z-K8i=38lo4KR1~HmCZ&s2d0D4-8uZIn83ImW5F%(!0#7>e>GC|)($~t<7R0`Zrxks zTU4UORw9SNHq2k|M8Lm8n)qvWL6=mDrGcTFTARkDxHA8yOy(L{SqyB z>+G4JdsAj|U!6%j1J55J{NCvD@?58aKh2g5Pm`Wz?u&XVMz^HDyu7UV(DjJ)<7)ZO z>Y>SU^G`nIJjtALc3<;g6xwqI$&WcUhV`3a-yyEAhkxg#GWaJ82oe6)vpm;+>ibO4 z^S1oQt6Z@y&u6U8rqVDlemCncOwLGmoElkZzy^E4~b}x;_7@lb*`6g<18);@s*~2@yv!@l1IHGTz-jUNd(nyYBh!Stz~p3pazP zs!RJ;ztc7vX^K`X+iyWXQ)nRNq8@Ttgu|s9Ih1kMS^>y3O&`XZ3etJ%peqQx=5_@V zh{1|wm-~J`=^m_1&ZaN9k-D!D#<20Es71)fG>ZzIu^A6f&1>~soQxDhBip~EGrRi{ zv}t%&P?tqpPVyFVvLGGI*}JhDe8n3;C}2gZwe_SVHf9S5VO!KC??lIbgUdO1C1l4`4T ztekp%1L9dj2TtL_c}-Vy*e}~tS{=7v+6?KjR(cY<#1h0TOKr9phAr6gO@D4J$jZ|r z2r_AqXc?FE+cPFl)7yXS;;{Z4_@w(921}hy-gYmezreKM*=(+aAnBbpJe#`m@L9q2dqRP9qz+O3qh{=(57 zcSLJXmDZ5Sb_bf9Y8#~+lRJZ-A1`(GbDCZfaP;az!{@r1V5x%1(Ni4qw-L65!6YmuvWKiFA2 zd%9Oi75jVYP6kXG{JKc7E(kA583HfMS-G1vxFu3rIasE!Jms^w+-}5AVp7EndSdCu z3mS*(VsPm7v%myUMMExAplvpc_pu)JJF>&~+4qgKnt8+YKN8iFX@hdlv}Q{r8m8LdU`W5>Thk~; zx=^`FlmB}ak{#=h$|#? zWKT!171rWzq^-Utd5ab6a@psfS^M*P_|Z!XdlA~tj$)m#it8qlkP&(IC56GbYZGaU z$&V!O1_y1gusMgf&3lQY(^LcHt3S6%o1@uVpnphRJ9<7g7$x2N=_^e`Zu`YRrCLmx z91f2|)`BI+_#72%aHQ;Bwq$bQQ>O8#2CJt|ck%ug=qcaz{OD)(6Rf7az+qq#ERXZ) z^tiYBa=59wP+l-D7ZtM=e-3+nwDK5J?Rz?nO0NHorPeIPyM-m?!QLv?Pl@Zhr0)d; z`JJ(d^ir0T@8m6<`j_sZJ)A7R>|!byZ7Xq~+T*lAgriw%KMqqEnkwPc&gU(em9%_;#Bk*gPN3W_tZAqh^OB#n}Jc$ z2iB|D97I2*RrtWOt-wLSiy)d#3oZ2wKCt(OUI1_ueuGTKO&>jrX(b^@1$}V z=qW~5!iG%0{%-H>BmF3+(QWTpy^exWuOov@tJc&LVNM(--HpxfjuF^-uu+B@UyQow zxsxoq_gee4cK<62xvxu+hLkmr<}GZm9W8^(=%ll~@;DiqadpSrgnf3~$J7RNepkQr z{F%j9x>9u1LM_#v$KL33Tb>q5MWEp5(fN;o)nCm3ymc@KtiodrN7X8^9`RUyGlACI zolL*`ZPi*@GL?!%mO~9~iz8vCud&2d_VO09vDx_ixo!<30-Vb{f)Wv)uU{3XJT-GV z5Y}jAlFn&3vBeo=NLm$DixW-C)9G`=`eKj zmX)Nqe)c_Enc1KkEaqAd^AYQ5@_5lSB{ zM%)@mMGdh#v|3&*gR=Kx<2pJ-PDV&A;<_{475eerICV)ccyC2qlMp7cMq8$p;N;1@ z>LzG3UD!wHKFKk#B0s|bA%&ObiLN2}NB=XI~B&7elH$#uCO$l~*;-1a=rg{=x_Kx3grMcMHRDKHpH_RBs0qC@ z2Ul`i6k}-IOKhib@`cJEo`Lk)Ujj>G%MXx8#CkMQ(3y>_W*{&OG zRi^E=#Q>t9*LuWQtK)?pigH6UTe9SRmL3Ja=eNUf)NNN?p$v_oM+Yc%COA04_PPpZc^&xX!5RMhQgVgx7NcrWzdQIp1 z>ml9OjyIyFh6>#TV3!jA+y9Vm!aq-T(ksuVb8FiH>5$)m4y`>fP5$9T4HMq-xC|bd z|BByE8HoTd$pZ_M*#C#Gw}6Ur?cRrp5$Ti$X^;{n1f)d}qzt-4x*HS_kW!RV=@113 z8M+&!L>dXD8v!Yiu5Zsd=lA~4>wC_(maa7{$KjbL?tAZRUqRlykZ{-|Q-NQ;Z`Y<$ux zdyah9&n8>oJ-gaspnl}*uwOy5EzdqwBhRiO z`!qvt+I0 z``D?H@;C6$S|~)O)LJJta$26vTqeR-l1O=9U>A%)^=@wFy+X}0a zMG*colIDF&zm_vGDO0UZl=&qyH&8gy0k|BQQMcU z%Qink6&{E?&^WveyG!u~Vo`}LQ)we@d;5BooI5Tgloo9<*<*W#WUvVP+-c8O^Ca3L z5r1wbk1xdgDRnv9tNsGPTI9C^1|$3sY;Uy@3v5k3r95%#R&Mtj%L0~qeQ#-3ZV3;Y zY6nhS8&_tFZE7iRA*AE$zW%(7Hu5~x*uIhJH(y0jQ>yRdRL_!%*kvyZT)d{=CBqmr zYhsj))WdJ{`YyUtIOmvbX7r??XJmUp|L#;dm6=3wTIk)S>pkyJgiOaG#(o2l{`!&l zHikq0J2ApShR>&lY66xGh9YAdcCq<`M8y#)(hGLUQ9mkZdz;sSs|O;^ju=KB6|FWe zgji9ImZM|goTIdyyFM1V6?i>s+xbeQ@fboI+vvYe{C~g+|4zjC$@DHo;7Ut{N1 zQg+1RkLzRl zHFH}@DR0Cb>!X`~z0}7G9AbYriiT|q^(R0ZUmu_p+nc@@<~} zj*tEMAG`!`f@(Hnh3%QYeJy$W>a*R0m+5*FN#5kn8cfXod+3uy{|C?g&jF$UJEvPN z=ZuV|oDAq&f`!sz@kXwG75!(>15VLRb_|CZ-fFq5R+r2Fd}UA+>AQ&Ww^tP&-z-`b z`L{PYlAz-{I`(%g-upMWjyXcQl0z%3GSi9mp9;o5Q9p6SIOK(scw00l{PP8bE7?eU z!~f$LtqXG%{=a-sz$dxJ?n}|O^2r4M`ps+65#z_l$CLi%PB{PerC5opux}P%N{B@=Oy57rbn7LZf5 zIesh7`~|KS#6$(E6W6b|tI!H74smHrd2OwgeO#LU=aYb%lHY>3ZDaf0>dsd=_vgPj zq}|Td%yZ*{nR6U`vJ&J48m$gOq#9Ew5q<0d-eP!MT%1rG%;kB2uNVtFWbLEM-@H}- zcH^?6d%DiKOR_XIgXdz)*qB(OU?Ay4xx+Uulr>n==uqA-W z04gRM;s`U^NjX&Qc6=8;lP3td#_9v-0hjzT>kC?83hb!e1ytf^`wyuMn4F>C6dsg4 z0ZT_66(C%&!t_1LtUW>z?6orPE!;*~yshpRrZ=PL3&l1IT%vB@qb?RebUcq(9im-G+Pwb1Zma*?YXKF>(iy~)(;sz!`AIGC@cDSH_kBsL{eWjTdwRNoB8N6+id;&8$c24c^Qc_hY5!-e|=Qi1@bdFNV@=vBHPnt9$4kU=0)aKw8#?@s% zUU}CKSe%Lp{F$gY9Vev9>NS4PNqv+DC@S+5<(>|qy6WkqbnjJ?*g5ZK4j!CuABsI+ zp~xtX1LufcyvB0s@3*|kO-9)ee3L=48r0AtAX}+Xg1<5WgW)D?*bQ()eGRicMet$C zRGX57T%;LkHTqOhBndVNwmzz#;;OU&=M@|h=!DuYAHdgpFuIc90X@z?{DedxM; zU=TQ>T)!~clOpk6(~bCZAQyNX^zA!3#RU&d+~R|G|dbBWdj($LbCcq zz1Q+v-HEl)I;G%i$P(Qln_|ZeNlwk$_+jt$J^j2fadGjC2RSkKKpYehZVMJwJNpo? zXWqRc#8qn!8Op;ab*f;X@xC2WeR#T)gtWNJO0B=F)B`UQXAwJ{j3Tf#oLmb}9NY2b z;}qo9FOQ9H_Rew?Uo~>ItjDG+ZP-qk0X-E&?4P4ErzX3YF3r}M4X##!&8Xz>_^U{XI1_kaf8)iFYGaS0v>adn0G zryD8u^sclN&b?_;aX@NLs-ctIfEgQ*15B!_-`)cg(*?MwE9u>4k4RbM@f9JvyS|N~;D_OG^%l)r+C@A{y)cVQ z?38vKiitPngzer*j|T-aeTHn-Oyj1&*zX~}RJ}Y+&O)nO|58>fG^Y8}wxoViAD@`W zw5uxSw?7mlk1lr{yJ+<=tbZ6BDRS;rJ*$4U{B4NNB9!J%1 zlF{yjeeA3FkTNcznl#r4oV$hKtu*@pJRaN#QLg@$GpRANh}o`h3>A51H77uLmZ7Uc zk!jNSjy?sR!?R1eK3T4FbaH-=O;&y>Yno|s#p~w!|1)X+&+34>c`HKvX-R2sh6IXO zLnVo@f5{x*E>^n1=#&&>yMUq<7xj0Ud8x)`%3*hwf{Svl6wAgVjlb90Wwc@&}nLC!)Ka9Q6iEd``rr8h7RD-$PStT zUwvUVI2?U+zYg2WK8Sn-?aAi3f8Ky=(-F+Uo8@2v%W3eTw9TW&YwIl?5GEh`49kKo zX4d7T-5(}fA;^@%{XpXitA6HO+!!9l``MhAKGF|8cec=QamG9$q7hdxP_Z4(=ES4C zJeYC*)9vdXKeSaI`txJ~wV$-*NwZ|jbJ?~bP)R~h_vr%-$->I(T$I(cyFqZx@`ivP zQGcNVIQKSV`7+l00N-Ijtdwtx+Acyz^8){J+#Lw~UR6)`WuKIAp8HwPe|`TZ8(hI8 z8)8LQ47-gjX%K|dAretxU-L;OQ=bMo@3R>mdkxeZJOUPR_^|A5H}x0GPj3p&;|zzi zU{Ad`ynKfte|_`k&zJ@7*v>vCS@ZphW)1Cdhx`Yj1bP&mGL8N!pt zRCe9fT{U8*%z8{7r z%s*&28YL(blw|Mw`1q722%OHe3JJQJ zCxx;2i-c8Pi$Xrpkz8s`HO_(cdeFKSd{NZ`->rHEIZB_ zwg>8ye~w!F(zU;Al}g<3@g1u|JmV(^*t+TGw>HJGm4pOxWUbnr*((!Hf+@919!vg4 z-uSDEY)rbrIAAX1d%w|z{_m^)-(NclqgQ--u(s=whMWIy*~`-^%+n#2#{b+N{P|%f zN{ugM*qbRXJ^F`U;IGG&*!a1y;rYM+^8b2GO!!A_`Y~P4<6jCscsm>}yOvi?uap*K zBmAwP`hTy9RT1MKbBsKc{^j4{GlWo8`_A&x?7vm*AH&gJatBfB?Ec$_Y-*A@ZSU;( z=At`o{l8!R+wl-zgbo5kZ5RLl{L{U1!6fogu9_6(zkVS@M7%yz?Wba+f4-Sv6-&Le-vN&b4>$cC{Vt>QISVi^gn+Gyv~NI*nz4e zqTdHp#0=N>Juw1Wst3p0z4XI44oh?Gk!--p(JX^ntrL_+=AiEzmejfiN4*RS}D z46nmZ@m?*Bu=u2#R@fY#w0lrz<`~vhqijbll+&2Vxzte^-H313c!(tT9$l8Kmn79? zqF75k5GR{j>WZO_T?+R7jgj`}@!vGSSoibui#FW_HOxy?>tP9_8$g*@ZK!3m(_3 zk9L0m-?|&E-2!~*K$R8)6-_6~ZVybi&?C<8&4PrnNyB-09~PnqBRvMib_e_&P!XVd z9@ENpVn;{ExX}@af+S^CU}O+FRDg5O#HRG>Rhr+SA~>%7xOb$L2dO{;O)RD7D4y63 zBXR6q{}R#uUTy~nNBE14)5_W|T!L}hBsmYEl%F9c11~%8`|k_Y@okRcYuO2;z*>4oMZn9*7l`s@$H15` zc5aqGa9vA_xomLVp0Kk7}#Rt^eBzQedn;A*tJL}NQS zWUC$r!SS77Cz;{4JTS@w3n;(&$^{vuj1#vZvJ$CVbsJQgEcp=&RzB>?U5b=8DG)Vvmf!xH9YwI$crV2{RHJ+<|w=Qhd2yw))Bc6jS}z6I)Kx zR;EsFxot*U)>TZVx&vcK|7JZd{B=U{f4m z<*H67AZhQN|FNQNq4zI!lJo=S>2U-%h9%Bb>~G>Al;VEPrR5^RAL0G*q3(?y&MJz< z|MLO>^(rVdIAWxXPSJK|u03PL3)`W{YQ?6ttWF=@6MNj`Lmqj>;@9_np?rNqOpU+XMA{m-{BvFi9MEK+yguffH`6FCz7R_}NCD+eloTT3A)#Q9O*(U=)y1O#m_ z9@?cR@LQ6ydP~!ZRR$#zX@>+`$|#qsrd+p<)nql7Zqq5b0FvnXv(rcJY;yV%l^;jr zo>TLGhJxbUu)jsqE||Tu$+qDy<{PIOP;-A()eiV){jDJ;y0E3DK8XZnm^g$pBwD+E z>?M1!cUkt1z{LjTeL3P=`0J4m96Qb zz=OukUFjP^Ey3P60QgOwA}w(Q>0A1 zS7M=JfN5IIbi}1EM#5qb*4pXIJ(<@OOpENk@sktwqkVi!ZAR2V1L1wh6Y*{DEcbyd zJh~*J1gn?qDqK&n2nLjkiDzI}<^i`-l3NZ%5$uaA+7Y8J|H3M)b-;+0_X>wyw(Hca z{FyCmW-+iet(eVx3?2Z3t^>0khQz!t${+mk-$vYqa!lw8XNzd1gCB%U@`DK~N;%AU zStl;vfEDmc%`d=hwk8c^4i2}X_`*2Bn5T&@Ok86J1`R8-iC|ZOLduXjb@F7?jRfSg z79fDsc!l?se_zX~N$zI=^qdMaH-d$R$Dx5lP?*nXL=9c&U~0_J?l(<5aBFNtzdl}7 zvXo&s@$g=j8?{OyTAUW^jY2+!#VhWk>MxkIO5{~??$(90J!_W4&dk4{)$WqXH~cSq zWEeYfcYFI%a0%K0i3Ii7ma%#8?97ZwjhOnFHp6U_48Dsd@);T$MERz3)MPNOkMYpw{hJKvo-It_ z&)Iko!mb7iUc|4yhdeZ4vtU!85|IfMjnwiDaU$2<;u17tNeC_pq;krS)k<+yXmfE_ zMgDIl(;xlAj7lSKF|OlGozw2Wbp+BFGm4Gf6B82RNB<4y{Z}ipsY!|=qY%V-J#b`G zxeRKM=`v#JwyDRb2e(k_K~$;=q-+mma7K)SVr-P{6RBM+;FLPxjb~Z^k61tuHM%FF z{noVGAB?0h8w5mXaDpQ&R7EL z3qd64!sE$*5JIHYSB@S-=2>kbyyZYzkUh&5xKGjo$cVtueCa^)^{2L z?564`Mj3RjU3pI=wSQ-64@$mI{JcH?qts_UhwiCcBlm2(Q4y-}Ab&LEpybJ~`vKuO zI{fB6tl#R2@gnFYU9>M7RU<=F#l4w3VmJhH<*JqdM*n=cv+6zx5lj(o+eQ#XXg5}_ zva~QaN4C7!j`Mqo-?+*;O_)pk=NY+;SnN&X13!h*tmpCe_M_!RhI8kG!Lzv&uq8Hd zl)hK(X1%JdJ|^PvOFJX=XaBofEIY>{qQ5WEUMYz2cXHuvRE`DP_)d0!ZV{7-6rTAH zv4c$mp!#%;OS+)2_+_H;{4MRRh>K541=@*(6>L!S_20VC>3DxJ(^{qyPCV2lxJiyd z$!AcSg&V!5fyXlsUaZXL3F)Fxk2Ux)WZs#%(PSYOyouGA2Yhj>ODTc`mt0PsS2IM4)5G;fI@LnxtcqdV8WiXFL zq1;Tb(R43=VmO~h$Tlp26J^6(?7eN_z9z)|W2{DPwkM|de zEcQs1ltU5e3dLI-E3^pG68|$$2oMhTuSrXrN-Czvhx=xlmnmwd4j*OWZLkLF?guch z$b_unXRTcuFS;*z^h2QJ*(;FP zL9@*)b-MWuP8@SonHy9adDYs@lfI93gl5-v*2c78lx9gUENt2y!7K4_Hwu2ldwCMC zzOVe>_sC`zv6Gogu<(L9phTOy#9&RT<=+ZPPkJ=M4m{kk1=z*`_^!+XcSIfsG5XJD<4VW1mX>TA~D~PTwiFj zGob>B5tnn(DKrky?PH?l`V51{Oqhyk9-*d0;Kjmg!UN6G;dRf{ahHvC37smJ%;U{w zA{zpa(_?RHNYH(LQDD@@v8D(z?#Y*p4dOuI`*e{)1!R;=V4e69TwJopP_u|Ub|arH zh*QxVe0b6$w(gqUC4j%OO7qlGaov$_GhStW)JE4(r?*>Jb zV9m_`;I_3_5tnM~uD*}KV+yDUMdQgrg(Ibixl^=knBX$;#S8|iF(3IBud z#Ma=ZBi3x0jCu(#%#8UV1j?4O?F_0EO`dC& zKD)=Y?|ptQa|}!7x?B<_PJ=SZCotZ%oDE>BfY~x8hg2Qh3zrz7s}VL@IcO3!{k=E{ z5Pt1XYqnCG^FDT!IY2l`8U)x6z?6Xh_;K^Oz{qzPc$L-eE1$s!fS!KXS1$b2QpQ4) z3u3dXa$;>2_>E%$>>nF{^>AlZ=WSBim#3~y{Bvem2-ym%dCKl-HF%23lEq+ zc{-WHqPsE~!V|PMRzXCp#aUS%X8yodkxc(YOO!Bx$o1xKQ`Ch=Sf75yOBwmW@@}b6 zZDK$g=7iz&&F)M23#4w{ZRi-;ES_IgW<_G`rHNDDk(L#|X4$&?HRlVu0e|;3ldXeC zyIndZV!o=d`1y|%9Gwelh1U7$nr`&Mg#?3K%^WTU!DDG~jyS1)-?8;OPRpo|eUoN} zfO>B;8@{OjEqL5Nn$PT}q>B2=+YBMi8^jt{L36191vgCMSqG}!Z2@5nx0o9%wZ8BF zFnZ=3KcVEdH+y}ruR&@A3s!28I2*Ws%`VR@yGS7wFba~S0d+ym$cF_Xp)A{Z6iJ^x zu0Skh+$GfF=c;KJ*&4Z*R+CwBm_ETECDT~H*1FxdOB=f;3B$CmBq_BI>N%v+#H~~) zcd!rw8r&GoKcvV1VdS6)9ZB+x_t>-)UR{bVwfZ(&cBTBq%)A}Omn_wnFgpA^&GaOX zE9^a@qWF_0v5^hf3NR&7@Lw6FtjxDExdgyLw5PjVvh`tK`5>1}AmOt$BIrl@-bMF=!%QRs=Y)W4 z#3c}Z0T|j#aQ@1XlPg;p%D9PhX6Idro_f_R#BF_C$1mwPiIK`1{B|;ZLwC6n*Cy-q zF#W*4!H8Wg6=^1DJIoVhjx{cPVz@u$ltflUJ^|MKtT|tf)9Oml3g52G?wjc7enhj5 zT7QML9Q7*dj_!?f(XkhnFE2Y}$P#DLoE ze%#$PA-t5Lzys*2uv%vec#C;IoByzEVqti2^sFmE>{d^zD0h|1QVa~mRyxIdniUUG z8AMF&H|cpL*y;W_I?*cAZ~dCdN1dEhL%)DF?CgjVP#{wdb08;0ma}G4Iu{@yPozQ8Mzm z+IO`ax52dadFG9V08A|1h$$xRC&VlL&9@+mlU(LX2mrR$6G_4jw=-|QMp)zphhK;V z6T+MX?x28p21JNKwOjPb(Jo%s4B&q3AWW{KlWitKvGqn~wv3YHXkH zCi%wxktyif(w{~ej2@F8rl$kUc{}sj>-od7)j+GL8jtk>Gwh#HU4G+5^3)<)XWs3- zHls=6IxY#~-u%T03XL2XeepJ1-gpUOp82o9nFLGSzYlBgYAUt(JIF+H*XEn#JF8c- zKf0G~U6Q3)gzC9QNV%K82IN?77@k?TwuMuw*$ifz@Xc=fL3Ky>`6J!9C>!L33ORTC z?8mgY+NcqG>@82+isdmhH6ME-#pv3)$4&S41q!Q#q;<2`o@HbL^2(uhu0FdvWJh}C zb~vOu6aiNYBNA{%gOq>Y4mLlKZ0>Du-#1b6il-{QIgsf-h%fr^N48D8^doyfgKa(p z73BjYfbsn7jR9(WjWXpx8dPX@&We||K%QTEBDskC%3#&Y%zpHeVPJjPzPh_l|1n(b zEyG93=o&;GAFEZ75HMO$^9h)`M@34WelmE?cmOF@v3K9yTHbf!ZyLi<7svx9QXr)P zc0(Z%ui$=-z?T+9GPam42Ez~iSodCLsdRm*R*4o5wy6_+aGhRy6N^BE)vBVgN;D{J zoof1=-||4FCU@9PY$o}J(-T1=SI5zeoL{RWY7~5icCTIAX)t6&s?TE{9V4HOGokY# zpj0hZLbQbg*jSF%Bs^x9SUy`a&Nr37!*p@afGeAq^_U2zPfzYa47sR~`|pdEf5& zC}u@&=8Fmta$B8L0h?ql)Syk~3D$CAh`WH=r1%FFRPWXKoc>0oY3noZm1hv}{ojz; zbSCTc8S>;15qkUcD^Bx)c|}4EWv2Tnkt+&G!}b|RBNIF4po#asSEuVf^dqpG+)#BZ z&@yYx(;?S8bE01aVQ#^%(!0t3%qpy&9eZk16hgg}!J9Go(N(k7S45Kgjqlk_Tb|Es zlBPmxF!j#brGNu=!$6YgLU3!mSEFi=ilt06eQRRXJ<~ckKl7mk8nc?u9-mNskUReU zVPFne-E0B@zs|wB2jjdyH8=CE(fg3`h-$nJ2OJk4nS#N|-DR*Dvsp0iq86|a%ym}6 zFvJ0)p>y~^&3x9$A77HdD6U`R5{w#^ooJDB>vlbS8ccx~E7bvxT9#M+Jw7VjdtMT%JZDzveu}tLz1QZ&my-~5v0#=h6 z#bw>>8+_ZM!3<)@jGGg+8yU70`SG4D4})iNkAL0KUpie(a>PB>{f4Q~*e{{{Xs#`s8&H^E zgF&gyKDIvEvQk%spAcsnjCqhs$@mnw>-Zy-F^HXfcZReMn@VtAe!g_~?e`Ld{bf&_ z8^^9oy;Wj)2iUh~^Y9O)ozD9dhA{K<2Q5*>y&uf9rEF>+LC9zqOxCGmW07ehNqPcE zy2=@eZ`I&|t7(_|0?-{sMZ`N*4p+ z$r=NhSGTUv0<uu}9=RQWHZG-|QUl9AJPmZqdc@>trZXXfJ^vi{5bY6Y*<~>}QuYD%6J9Nh)P0@!@O|pYQ zc!?4W!^d|_>nN0Zj}D36exTseRw&a$aUVyo64NkNnNE*iKCUrYpYAKO)$;8KX?9zZ zChFPgGP9a)RQCE5#T57{U!gCn$tV8zo$JqyY+8evxMOpJoH(^~@&+E|>E}0`ST75r zGT@2#&ckaWI}E6Ll^b-;spsV)!Kkzgn^T}XjbQV584+*Ah-t&()xN;($_P49W(_W_ zXd&B?>~b6`8RKQHgO6xCKDkc_d8PjC!fH}5W!4s@dCX!SKyBfVi(dkIm?~i6CyVM@BLw) z5LPxc(FKxAsu(k&{%OnFQpfmDS$>CC{==yZh)eUI%DtdHv(P}7jQ;!P^%pg#G53al zV1?GgZI_FGKVyG?J@d>zt>KKD@~fae`l9haUuF;jv75>%N^l+i`~Uv0*PHk_ z`_#+S*Lt&lM+E*;_Ar`~8moT^^PUg|o2&nPm%qOVuvBj3V`QAB_H0`G_m7z+*@R$w z#g-%?%ir+Qzv1h>@%}~`3el;~ly?8dor7<75&h6B+-oFR;NSe&y$BJ-kinRZ`fx-M7)9cQ_`cduLIR8tBkTV zViw5bgORjA{@&{;#`KbIUl*zhd&I$go=)7U z&t?~>tHrKMs+{^6g+TC;e@4#7j;(Oj@7OCxJN@mCv5HIM3%*Op;<>O!&7Ou$F8ylrfk+Y*JKC_pq!bf`06JXbO1I>$ZbOS1f?93MgrS#)`V8pV!0z*n|h-6C}FS zTXmkx`SdS3ov=5EVRz?aOtrK=2i!bq-Js0u3{b;SC;o}o`loNdI zc8}Pou<}h*y90}?hCn|lo03A#y-fXxP6H{>z{H^1RZx&HgE+@Mo0h|aE@#VxfO`XC z6{Ar`_^6G%ZxNN3h+@w5KH16)k7EVfK7OFbRKfZ>&lhDRdH9`Wh^mxkhVY-4mD;z6 zW8-TF{F%^sf97~0bvGTn@fHSo+<^ObJs6N(C4f=q0bID3ZhG!uwV)vb<}51(_~$5i z^;t0wiK3te#Z%^ZC-;KlLr(KA{#!kC4&5peLWEP!D0~g3z`0R)^|KT_+5n1s#zG~J zaw9h%|LTlFt8=OMLABC*`qrT?H#|{ z_dC`*a@;AsV>nZqyDvFZt|d+XPet|TjcRC(aY|f!4uPl@d>NEQAy^BZ^%-WD_41KI z9{X(E2Gym%Hl~cW8RK&gWi)iQPH>g(ul}5h6H@R`Gx}Otp%lxLU^iaaPb2H(vHN3! z1*m_~!joRIxiVjOn$9yQK)@0J=^iqk-z(JN@9 z3SbLGnD-$PT@}Fh@=M;f)c0lsDO5Cp{aA%!Op792={_*{I3S&TI15U+^c!W45B!`$gzNPF9Rg??1d|72_+UEqR6QGo*w~YFcaX9hpA`(ygW4KDl?AX6e5@1>UoLQ zH`pax7zo-Hg^9@3+dDKvnRefbF-m%&0_uRidkFa}H{uNE!PB8B+ zc!b;rP~o5UoB*u^|1=Jhk?mv=LFDvEmqs8l7lfoe9|ESasj6GKjk|4 zGW5T>*u++C$E6HcN~|Fi-n20v*ER8ty=#xmXkFsX03nUdwH1BgjFb4a@p@l9&yDpX3Xh)YSjf`=oFN?-G*S{YhGV|NSR!$bV3scULX^o#n zrP~7E&B~0)UF+2XH4doB+lYEA8$8$D&J4aD`1xH#imOoO>w+bD#$^-n*K>c4(F4M6 zF^*$Tx-5R??8~_B{WGIkLprD%#x+VnaYP=&LaS8aFd_I!8U=^|y7hC^wx9!gbzaZ) z$!OqrUH5^w4#y#a&XldWwp^(yHOT{3uaX}{0-V>Ht?Y_%cm z8O#e`LN^!u!4{i)sVkKXdF@o>C=>)C;@a-1<|fCVFwpzqu<5H4uGuGIxj29F=^%WQ zegcQ86WslrI;k!UIy8Ug46&fko5McbS_5 z0D5y5goZp8(I0uxf%)yL*w10L*_%&aOLTdj)&6=fZ&WrCI=u#YG%uf$vEQ!s+G2$W zfJ6SrFovQQae7mCX-|<+pH#$YS~@K7ZBS6gFPq&lNtR> z-fKwZyIAWw@@*HdvHaYvshZMYNbR-x@$S}(;RezDM`XOZf&~-ekypLx5qLrIXaz=L z{Y9oCti+S+OU!c5K0QwmV12#P8=L`3Y`LWZuo~~Ua1RsEa-z8ITO%ayA!grGD2pR)}&8%+Oo-g8oW)}vQEjH zQMfvo^X{$~2@;*g90jhLcg86K*cVjV!MLMaVa>-!W4}`AN;rxQnwdAR zPiW!Pw^9dg!rPqOF;`TjU*ata5c?d(7*D?N@V#2HE$F!aOx$fVO6|nd&WXTSSa|Q+ z1(h#)j%Ll?KJNn-YfEDBQ;EJj7_mD)LcdqET^Haj{blm>ILUq7UP5BZipZBq zhHN55k05#Pnx!~pC1rnB&3ulsSFK-@H^oHW>ON#H?IH+!{9K$bdiRWsBSRTniz3W; z3kw_lQ*P3$^}an&p7v|iev))W6KTJ`ey;SX`x=n_K?&xTR*58j)>T+3sV$W} zVzqSiZGyP2h<`NzCsl1}6}_!zkMgJfi9usz|6(N7{v(Is=IYQ#=XvzmN|)>>0*I`h z_YT$24vQb{;f)*ZpF&}|^s^orN+mYz$@(k2C}}l5cS|7h4-}^n58do4!4--Tr@gBP zDfBkH;4x=vqhyTdQdb;$jZRlx{I^0|*A6yCET4DmH&gppgahT-{yCYE9F#+&kh~l*^_h<0iZxYWlZ?N0REH^)9 zd^5(LCWv#T{(+wPXt_15@%~-#l^#W3{*p=L#jzlUZv7>L;0iOpp84%KE_&meBIOZV z__UJ2rFA~u+t%JJmCxKK>s_~IThU~t^IcVURJl@0Na*Ckp*y+q#x9|Fl%vP|bG+h` z-EjL?{Dzl{>64howpJB3sTBu$H5~!}dPi;j2`9nxmyN{L+yxC{-nPJ$M6M)u=uq{UOlz`Z$?KY)HeM zwJ&j3sii#5QTe@iMV5+LT4Mc$9gisA;9#$lyFq+ov48tDw+3NjJ8^{GLKpq9uOnn+ zMG*9&r)rFot;d)QXC}i01=-86Zl>?q?3kj_8djM%SHl0C-l6Z9Lu~xAvPi zl3%F6dBmmlHt*aq(XgCjuW+hFbBov;zq-TGw3#PERS9TDi?u+aG5As_KfS&DKF#Kl z6!O@yZcDRmNMh%0K^k8#v%k0Qh;yFn(r0>OY$~2|7WwCWM~!}w)Vd9cp^pYcsn-Qc z2};e5H>vK_dO5E;rdGJGuC5!Mzu;StJ(N*lNzyB_&Wo(ruZd=JrASE|^0#k4pl+c7 za+pG0S`o#Z;eN&z$KeUd5PrI80ax5{XvH0)XiIk7>v(fAGw z`|Kh|jh*LOeL1@$Ho2)6FD4?k)f`8L^rXU=X}L9d3Yw**>R)Y-65~ZlQ<3;%`EL#U zAp)suSR0Dm!pIy=*e8FqQkp|UAPOq*V)Ipslg8u;4gPxL+d$zvC!-sYCURLRj+Z>X zCX$|(YQAwFlL;tSTHRwQH6EXP8;*JUu?6`d2WcpTbR+D_) z_co-JFZC__H1FKmACYMITr}670V11$blLM2r%Cus$u?q0!xCM;ZwG%26YG!G#jYA= z(~Y*u03>YQ`k8#?e2K@}=ygk3h4h7T`>8CFw~-gcxE}4UNBbOXKV@ok$yq}e*ol-n z)b8Gc__FL#@5#jtaVp1?ruWqP_^9~+A%iF~EwRkTCg_3HY9J2jqP?M%-HZ|G2qw-b zkCUE%*)K4(cbWfAk|1GO{zr_n?x+3pBapJP#c%zSwXVL>gLc^Q4uRpTfU`FYkB{%3 zJT)D!tOur6a}H%{a=|&Q3$U-)d8|#cg+&-t-8MfTJB>|1V)O;{{Rs4!-L zpg%Z6b(wLKZdwv1bXA6WvS{D$w0%KOWzgTq8!KNCkA2*rFSHJfOU{K!zNO+11-%ta zh#2Jrfey9Y#~?hZL0ET=C8v`Iqj3|^3Jo4n?+FUpMmaIC9Nd$~yM97;F+Z)svVW}l z)bC(V;~fc&tOY+J=I{hDGN^2;SwMnV4wN!j8eGm*y$7t`x(%m--Swd{F4$dM96HBJ z!pg9wN;ZXYS8;j7HibLl1Rw3%QHz?w6}~Rl{_Kc8QT60-=gUgpV2}kHM)b_aaB`LF zgOlo=V&hW`35J2AmbjuSZnDHxRFFrt0W08 z)96$<7+m#hEUITgSWbAa=2f`WrNXrI$u6~zK|6}`r-{s7&DfetopzMSmpUznprIRJ z1(LJs;IwFieb_?$^K;NsGB^8{DLnQ+8nl2Y5^UkDEGAk>TNDE8&l14-@_ly4%=-za&>%|ReVHG? zcsCy~ELK-kOp}Ccy=kS~Exb}(LMTw)xf>JWq{VLD520ifXd*ICx@IqUM;V&#%sDOk zA)mp%N`Y`~J=rHJ?P&KMx6KK%Vufr53)?KnPtSUi9P2s#qEBAoEFQPf2GmdD=TYxO z;+TjB?OI`G@r1G4t9d0n=_3fNGOT;F%;~sYF(SNIw<8QFkZ;i?t^8y|u|hOH?0shm zqwtf&Yr$JdBSd=fbP3jwLUTC#t-{FYDafrYP|;Gj7kC)S+SIOuFP8%;=b^7N(cpez zh+#YC5z%*1Sz9?3H-{-4K@(Z2U1_P^5MxzHdc2$_*~1>W>*4dw)1CK*i`vb|iv|lJbczGaz8VlQ@9;+)apw?kyYV7aUn~l zF$l4J-DlHt8n`jV7PTI0x(3a8_+x~rp7GN&=V`9+0=AhidTG{vqF9KfUOfCe-bZ^` z%v-sTM$=!t@x#7qH1x4SB{L@&`%j+NrcHW0I<9-fR2q^*T>2EzV78xxXg^b6s#3dA zQy;u|==S|<(;7`4+0V^LVaO&hkyuuRfUj$8m^`Zb-dbf9lC@P#oD!DSQW);ft-|l_nS;R@wbj z+75_peZKec_*5|tOm~n41Pa|3Xys9_bTXM}^%mF+ca`!wXpgh7OuLqCh5G{wpNet+ zh9kSx!-PN)f!;XYm9?)$vX29%8u;#122kD+NSE4=K*;{GtpENfsX=GTdo^FGHtdFi z8Ln>f>cK;w*?pV<&C-(I3J{!R%O`OXy9^lyR*;2pS~Yj_3U1=x*T32ooT0m8gPQBs zUG8z8QF^~5ERj-TaIyK!;sAHSAay%-d0jVXM`FgFF13f{qX_9b6Se}A`l0hsB$u?@ zt1TS?%bd*)y$g0$akJhb#8i99C#h~j4JXurS4aW{YTq0tKC9KbNgQPjAPpjVIGs22 zuyQy5o>S@uKDF-`3m0L_V<-Q{yZbYZ25{HZRPP>Bx}W?oYpg%f`Bk1jcj~V>TfMW2 z(&8SzQveVM1va9{l_yuxB}?CM+EijWt>2E^GP_lgKk=(t)-~Jj$)Tyd^DT7G)mT?E zxRHq8+~6K@B4Y$<(!ix%;$PlMee$ikm{WS$w(R?pfoNEP;>XefL1h>AUd}v}OQUx+ z!0YfO9o^yO$&VD(I`TDHtQy4%eVu!_{xfjfvq^(?aogN5aynm(*xy7Y5RH8AXBz?S z&S^eit{$NckBO^CM`W?5fJJNU%i%0^+0#1!0uF3jOCttb6 z+8p^jL-pn8fm>k&p>iXjr~S+PZpHB0Tsu925}0C&vz!!_^E^Nd4V zEMHLoEEyk7eeu?IirA;;BKVL#cAwL=olY-{sJpomkGr4m zo|d!*A`=#4qL6K@`UiDIp&(~M-H$!Cq(6(Y4h}1b7XBJ)e2n#XbYw6YfV18UvhiB-Zz!$>( z@MAqC2ov3~?20+juCSm4!C&}i>g+hxh}6Q$_qBLfK1=wrKrJCXIdUIx%#bIBKv{nT z`iQE~amd*1;iacpl8qbh_$lWaFl^?h+|`oHq6#t3GOJvXeOUI&wvu`Af;+T7#(_wX zxY=kD$tX2Ge1JmK*UfuGuyFKe`1on0tqr^Rq?pG#E|ZD|r`2>uE3f{jbeXPxvN%#( zif{qiURf!|+wm2vd&`Ow(Eb!v2k3({ANn#g;npez;{6Wb8MI-^}qm}HR#UhiDnTZ0^_Q-k- z7Mk2t`-As1fe_PmmNLHyimQ6zv%AXN?w#L53_$<_ zPR3l5AbkYAq&)Ps{n7xQ2kBT2a_?zApQNE*#&Uf}9O8f97GzOq&qcH@%5ry3Xh#Xy zg;a$GK!Ucz{Fc>C-}c$y+!~|682$k0vv1b#L7*gTSwnAz_mDo1NRe!zrNr%*DiD)_QxEU5#ijA{ zCJ%{Q0^1daNGwwJma{x4bDDpc(`j<2U79XtZtvf`a3Me~AFD6i#0-d})t1P57MFI@ z|IA9=OiTDDT%pKTos4rMK*w2g*WWH}kwEnIkb)MvYmIG&Vob59dYi5eLe$#ozj&rI z>tkQxw>DATNElrEA#Vfk1^WZ&hA3&sxsX(dTU(GxE>*V4RR4y+lPRYvwBF;_V_8-1 zHU){BZT#^5#cZZM!%b}x?9YxHP`;a>Y&M~`=sK~We|a^W6Ly!c*$LB3F&%w#xzp0v1#cp@rcKKvytl2Kjl?!89_ zyAs!7V5z2BYTQeO+oi6!*1=|Smq*2k=Ir)PtlMIUQ#(2lrSkV@N_j@KusHMO-k34f z*S&X`kxCC%JLzEsTY=OWAG6@yRZn#ScC(atlhxwoA_Ctwlc!n|{7Bdhy>qj7uSQ)q zF>dT#PuCgeK3=JUtwu1rt^@jcb7L{Y%OIPjlOCN)d>HpR3aj7 zh1i;)*Wj8f^sT18?xl{tn8-JN|NfRch$~ai=W6M1rVqErAg$j%P2=>x9N3dY5GK<@ zLdB;o$Vv}tUE3o3@kn-!>9g^AcxE`8q&)5OdViFh)Ym_0g(IHGO|mQ1YrJk~BCAj0 z@=7xHi~@*(4^$|^7B}wa2qta%r)QHqp_OChP#Z`bZOoH=ilYvGZ~DyENYVl^pVc&EdjXX;C^F(&!#T zGf8@!fmR+>R>1lf`d*{Rwor_*V%-=!<8J9u22Bng3eV|gk4AR4b%4=r*5O$MxAzD4kL3`Fw0=n!cIS*$d*p*S^8I zXugf4LVjw9?9L+b?r}{MmBrVFdYF*-RV5RTSNnEoa_~+Cy`>NEFAgc5v3J)_`QlHO zu-U6~)-NNjB{@gGyaDIh~MF~8&RiU+E)VUKY7eyvMVPvw?& zfok>aP7&_pTZvEkm++7p@LrUC`wq8&D)j?2b+c6*6#;^(&^dFso%``Rk=g#c2NI*u z^d*pz8BdQm^m)yW_1Ac*x6h6`s5`j#wq5(>v}mAy9>dcgV_a+G;}02_mKY$=AGKa{ zeEr@5bLmYZ2@8gq`ND2OT@$6DOL8@VwsW_Ad)Lg<*E4+Qs`v7nP|0+iU}2k(@no;_ ziIq929@Ku6+5H}G!an=sa$=qskGRnNbPE4d=RVS>#xPKp5590SiI?-QWzD=Qg*QIB z6Ejh4F>h)DJzkOC@b=2y!ApFG@&35WB7f^uZu?F1dXtB1h1oH8)=D2_mnlM+J)dCsarAm7^jIqq z_=n`E7Sg*uH^aB8)zctwEo62LHTXEr%wP<>k7spwhq95%h}`lmvQt?FwQpy)b0*f^ zrEz9Nyb+A!_13oMQRe&X*W>;CyPKxFQ!|ZVE66oMXlJr6WZJd`RZ2kBO#pQlBv?=#$@=4tPV z^t>jv_ka~p=>qJ*gX22qwH+d9Afk_dq+YCdt?5X#RZ0CU5i~VFD&zEd72Tv$m4Nz| z^5H?yg?r<(G3IgnL|iG-N0><9aBj0ey*#ScL07U84RL4@^AC+eBGSO{FftMbP*eG0 zJ8R0x`Stm8|5=)Z^%hP}j=U>sl~0<~9cRz4mKEps!Vj1fKxiC$FEA9*tPJ*qR#U;X zgK|Sr6fkdCLdC`2`7#$XXqHKVMDt9s;N~2+B1^WIziUxpM@5>39b{xykD$28n5O61 zH$@0Tu1SGcf-y+F+i({E3B}=0lqEj6I{us|6aLEewAc!G*VrwFX{7G6+iW*6teIO} zl1H&X~HD@Q=9z_~Gpdka>~=uvo{5G5h^Bn#?a9POlI9RT+QIcSZcV+UM; z9ymi&idO>+pXA=!Hp0#CXZeRvpHeFw;(opkNJK$Ut6IsS&+HRq&FPU2=CG! zGO&SR#(7L83$qIBv^yLRrFh;kpDSwUN#tf5_iY2bEZ4%JIwu?0K2P?Z0XS@@n6<~Q ztDwk{0cdrwtmB8=b{X|I7n^wDFC{}e!D?msa%B#+D=rrRB?ohrsV9IQfI+u*xM%gZ z0vJ*V&R)nP?gP|h&sKlrm39)ZNYOxa?)NLe_g@Nl8*xt!(?kW(@t8FkV5lkJs=dp7 z%Et=U9n_#hj_jxoTM=Kt=WFDqMbhcYROA4Dl{W5Ig!Zx7D;FP=Pf2%em-nz>&B^Y^ zz1~R@`AZ3m%PI?4{;y8Tfrn$n&h0sY z8`wyHdc<8x15p(@Y)m-eL`;V7B3dpWQv7)-kh7l zdc-bV{dMRZl7L36S!xsp><-d%o*pYeX>`!mMe?Z2{bD`)0CdVw{J;Oh`}6BA!AkJf zcSb6LQ*P9$u}AhdKPle`R2?kWRCZbW>;%0u@vWHycXKYmOB3QskYya6DjLugQ`i{E zZNK_7wn(J8+MosEvu+X7r)vbP+-j=jBfvQ|q6hrriRETLS_h3pY5;#&3RFLhfY;;& z3SFw)D5xt>-aSY`Lts-XZ?K->$+yyXWO2%n3?F+cK7SCQfmQ$4Q*a zgFMD($@wZpkHI1y`8jWB^4*SgCK0)%$%=K_WU!}~DbStN#|5l& zS#qDLfO#$ofPC}4U%>?E#6H-h%^QQ6AzL8ibMbpH{f{(poZ&!Df{#zo&*ItMTJ^?r zMX2N|n_mN_9CD91$W_hKQVPLi3J2;tEf2RzpPCIAcEV=1J{(9a7;8b!E6ky<)8weT zR&BY)#6JrVfJse&nl}whpTq+0G_WXan6!GMb(33R>GUfc+RLJ#pWL9dAB;o&Q7nX@ z*V1dDrWwfVU>qpdwGcVP1C15z|*k7 zwVjMRo#E4Ld{xt&P00Sd)U=-o=uDo^+0dY%@5XrPll~OG__-7cvs@1r>kDeuJD0$U z44}J3C5=rTR=GWC5jSwPWPR1hT77kDtXXZF%5gLBe#JN^q#5;eXEp)|=r*p;S>MOK zyvOhJCqW6B^%mfg6GXyb2ZMDAHX|SX;fM%ofd>HnbsexY|M_(c`yNvO*bJ()Y!fd8 z^UX=6L;!JOc>1UfLr^7EGbEa^yzbsZwj4aH^4VGUL%HL(qhAa~15?F9hV7<^@GqeuMi2^rL6p&YHo!{Kt*lnCDqx*85 zbV73`5iac%e$plmn%wnDzrYGn!si{JiH%4 zmHc#=4siDFf$a5)=`Dz4phzF0M6;Gc*4M1KjJo~HF1Se-PtOGjm%%uZFCSA2Pk^E; z_d5Z^pju|ENLM*x2%r)WxzV;o&3Z?QlzBV2P5c?*3$&52eNCr`jee1qf!kh}ejt4v z05-j;>S9l#BB9Z~0v9;v!MaSXAKa9_k8N zKAr2vs}_nKPBFZLNJcOTeE1(!5qUw4Ao|`qTL;*IWCBUzF!c<8Mm&Q0oS^)~F?lC7 zzSg>fl?1bN;?^fHy_v;gP1{Jwp^9L#SB{vIj-XoK*Y~=~ zY_&1VJ_i0-u-qv&Caq6;YMzF&0V*|ou4efwE?lAa9`cA)*!6M-V306@&UhFgMrb1} zQOZPr0uY?K`yYjcf*cUCwt*)3fQFRqGBEg(i@)*twgztfL#iFH&Mf!8#XM<+dIE$| zo=@-+knCy@l$r33`;LGGOaaxxkm<9kWRxEQP_1hh7yPGW&*rH443M-p#U zV(+G^>_#CP4Qz-m+Vor&y>5I_nNhlY_yU`waQjUs?hK7UsRkYVGfB4G= z!B6_~`eDR#yAez)Wipp<-Jsh-&SUv9=<8Z7$7OFT6|;@^?+x02^Q^H#+M+X7pi39* zqTUVFp*Ag=#zR``GL?JWtsTws9jAJ>n8*2feBadsX3iFxi3L@x9*ovt(i=|+2r8h+b(mOX1$>3dM*rxU(7=Hr5?xe4`T`aK*(IgKego9 zr^CGhKreZ?M$AF`Ey?%wu6ma(u3C%@DvwKT3>WWNb?Q6a5*X19m}~;+>ctVN-C)N9 zLC<;=o!)uWqAnKZH_(*Aw{FfW81yS=!Aq>BJC8J*tQ#0&o0=^iv;u*ITUUpTq(vN{ zd7!vcw;IhnYm9sXw(V(XI)~B`)aHUfGN-KUeD>NtY96r!bdR*CY?A6-nI||bK5wR6 z7iw2_H4ERVxbD^FM1^_}eBn?A;11(mn*fr^3QMC~pX*ZK&JCxwNh0+XL}Ezb;DBC+ zCU-2;MlkmBmwo>9s@+B))wg1WF{C?LR4}qBU^FrzVDFyi!lxpj{!v!9?gTY}<-XWZ zBe{gZ(t~D%xXNp%{JQSj*)dK$53Oe>j>RBE9AWyhmBb#%$*6U{Hq9Mn9CTBeH=Bo5QQMp#FY6n%-hZ|EaS_aJW-gC-5#Nd}+ zGm#kIUqvdQWQR?q3x;cd&py1=%FXD`D8d6K3c%wK349-U52>zN5U4@tSUOEIv`AnEGY-?l5LhrA zospfanPG%a&lGdWA!9upiJ;^3J5LK{44iQgAZ#((CW)bHfEe-I37IMv^YP*#Fbl}6 z^?k1J9I;TXKw-~=S^n?^rF@` z_b1r^e^L=m$gGjHhS1CQLF|$%ZY9g1IK@nH56y*en9YlGmd|U&bkV#{lZ;BWMCX0;yjX}XSD&=PNa3V5 zbtdpIY3=uSiK3)1^8tIA|r ze|RJNeUL%Tm=vZ-2LM}?a`BWxu(N6)jel&~pBR-iccfCtarV&k&}!lD_fZrH*X{jji7Jz-+KcY2G?i9e3c2eK$H90VOB5t(ohs`GH~EU-PM>eN849msqXVcTp+XpoQI=xu5K4P93`uH zs0}sdT1o76oHi0* zCM!{Nnlj{!qV#=5(ywrlTF`qq`*j*9xpo91jfn6y8Jnr&_nYFQMBoCBF6XL--H#SO z)Y)C$ysR;H-=ftXye&`IK0mqsx)0&_U^C-o)PGW)Jg~=&&-LBw^evei8`R~=$UR)# zg;O*U;W`B_q&d3I<*+Q@O2l0}WPK)bM1&XkrmG8Zl zw|%y=l~@m6*}kE1m``j0K<*+9RZ!Zt!N_+k$yRg3rO)}=Sj0F`xgDlWbrcKP%T>oeugLwDM; z6TOT?W!6CX(}_zpPVTQOf6Nvnf8Lg)~8` zYTO%F?ApdxhZx06M8L~3UH@8nv{2vl{E2p*N#WRn^;B&ytAd02cns^##zi6frv@eKy>{en_x2u}DWOb^Fa`F9BZ4Xv66v1LyhI(RIv?ekjLj&R58+rlD z$>ZG!@aA#MMl)&B!NQ&jCQ4;Mb7mz_Rj`9P*?sZ((37Vgr3I#tJxe4x7O0@lcbJy9 zm@2!kf=WJ?28jKi7e0@D-KtJr-o4cs!+LU%IwLu366^7ifI|kBM}(AiV15XY3ByM< zX1jOnzda&L6y*V&4F;{FWKC*F5iGYz;^U)SPkJn8_{JTWvhA?4DhljAk1e@@tFC*P z$h|9?mL5=|zu=>ldMzF_mA<$|2*l`-bW`hO3OTtV^HV3`O!F1MgvvaqScomx#(WUF zywgT~FkUUc*L!zm!=}9K+kkcC=@YEzjns5HwVh{5*^4Zc#R4`PknIAs(rnQMw;2CogoWJRwODwq?I@*ip zxptK2M`a4I$`rbvpQZ`u0WKFe-Go=od20LJ2L(F1apOmH{s$=|F@;|R-!|_b4LR|yJ2xZL-t5ZuvYZV zqh8k4O53p34X#q4zVxN(2is?5>uiNO^nn+n2{uSQdnMWMBlex@E2SO2tKuB*%-hA? z?zNP6)X~mjCyH)pFqNsCIW|z3)d{_Lcr$CUB5l5G943lyGoIjTc-c7{M5)aa9o4_K z6?tEuQEl#xCYqN$XtYJW59X8nl@Y z(HSq>eY(~dpv4rhbcF5986a7sBLF=K6-Lo})9MUJUUYkgKVR{9?zf7=ib(NlkEKHB z1%n$ST}JK2zAgefgody@-!Wjga(t%tO|OI&3v!PSx}ke zZ`S7_6uZi3$mn(0%hiC3YA3+pZ*9gd|DHy8QsQp;Job0A_fi|BLQgxzQi48FIoW3q zFlLBp{^3mckE>gc2*@2cJRF#)Oab7O6r0hNomS&ZO8tJVn_(2QaYy)HhxOO5iO7-; z0(jva8h2DiiBW1F^#mbjHV%&pb9=}Co)`GWP}n;w%TPGX3bY>lqv1s31ENVc|L7I| z#+T@g3b|ijeyGk#p1%fWf?*1j#u>HiuPu^49}@+p z1;i7$chDWkU;SqYeq&p14)Z1GL1TINrIgiobhe7>116$!{W?+~4e}2@_J|2N{C}7DPv;E#pO`2RPYD!p2etpd>n+~A z3-m)p2XX&CIw140E`FS95lP6hrmFH zq*L$woJu~C1~{H$fUzzxf5Gq-z;;FeQ$;7>MYfHMKaJf}ASERg;?e za4Aw3N9!x;p#p=id*k>eb0Qmm!pRk zy>Vs04DH;B3>1!{J3uTP_E6_8T5Yfc?lUif4Z4vtrXDibzlrZo0E}9b zV#^fpiqfFsF?<3E9-29x=jZq^=GY?vcMaE*ja&%&CWRC5Df@)rGs%FgMI*4i5tcFT ziGBY4BN2V>PzJhPANLYKFRQd9#MuZ27+|ym#V)Ht;5jFS$D^mtLdok&nX;O`RUqsr z2Xk-)4s?|m?;xT@f@qmqo?gEHJTg8piNMvW_)8?x+%07wbPEqd^@7)LbN5sCk8#je zT%Ra8AD}Ye}2A~2pjGV0QJcbj7=R5sP>5KOW>ID(EM?p-h#q80&UwpY$Q}j z_1H%e-lEAQ0G|BBHRsMIRR)rZ!g1OaI!3&T^5PP%YgqJ;0K3*n`V*-SET6+*C&0fF z1NhgSiNPga8?Y{%te^l%u;?z%Cjjt47Q?I`&2By@Rch2(aA=0w$`hNN)&N)zArG4N zjr+r{<1DB&@67k1QiJ@A2R;L6n;QVMK>;EQMq~Fxk$1!oXWNjSHp^5y+dl(fR|n{9 zKnWbG+qXMo1a59FZMYq`VquNuiz{c=TfXB<+8U3w%DyMoI&*J>&MqNLFos1qHl<8X z=Q=F%6r__{q-|W)n;YJU{%0oG8^qoV1qc*A&42LKW=A9?VLO&G&4qycDto%H? z)b@u6qCG_f3KR>b2J4Og66zn*48o9PzRBPsK>THJ>0zA9qv=$?z&BF>ejpvg`S}ef z0ceqT$1pQ5QNJHMJTvWDrX2*JGnccy)YXL*)Kw9hb}@*dv;kmH3>Mo6)&T4XCu%Cf z&JVB+(rPT7SKsJnZn}?z(W3(JpbuGWy&rMgi8Wqq(BnQ%SWD4lvmbrN4RSbDic}_g zqFdjhXkV_PcP%~?y;zCS&#+3UgpdWsy~AUq6AeUDHEajH>P*ng&jkH!7*C4Z>!uzL zVCTZzxpsMRIb)BjJvfkI^lP3c)1i*33EbMsKUb|hIJ@1Z7Tp;9)qv@IC~c2qtk_F1 zSGTV6Y_J_bIx>L&%fiCy1R?eK=aY0wu;(#}u6Qf$cLqva-8rg_cY}*w_K!Rdj&v zXr@#0EQF2bX-WI&Gt6qxy^%EONlcwuAIg#icW6dC7rzl`vLBpG+2pT&2gr?zu8ooW zOpuzCr@24lH22FK1(O5|Edq3cN!fI`P!95|$#QAfrh77Srq(F}i(E1%`XRJwsT~2u zm+Y10U`8;kLF#(ELrJez^a(IXWUrw6sw{|%Rn(v)>kWU$=p=E%A_;^6%C~r+%k^^G zul;;V_NBS;yoKLdVNdc@dPxB~^*xzg&C7ORyv6uBT)&{EHjnvRH9UM zd$SxAhHmVc@G{t{M;{~PV=Hxpf2BLwQX%JDqsE0b-hW+*Jo zX`oOsRfHAf&NP_rpTbR=_aS@P)V|qy4EnXvoyt^*Kkhm(?RGz5pT-P=U;i1F-l$^u z@PCtBkhdVz##XWK*G`U^5MHb_0rWyt3KXBGU_pER0hW>b*_|wr?r7$+0~CB(29T=u zybuygktvs;*5ATiMQc>;bea_3-l9@u*cRY=18jbI0farOsWWUYgGRkJQ$>Z#9dV+k z2^=<5`sG(5fHagAj~CzRB}Ezu1!Uh0M)!#e*<4^;*dZr)F@llT1>>Tx;4>n$>O=9) zGdih9`9t8yO#u5ce*PuZ)%NZATC%FyhoCS}^ZlVbAjWzcwy;%Bd{bjkifHu%#{b&{ z^mP?-Rgq@2ZEm&(^&|fE@sf06*yd%lSi5+&VAaK1Uk*|&m?Hgtx%X&x>f$}ecnq|# z7Ibz{3!c8iXD&MB^!0r;Tdry0uF= zOa3#bs;z#Ysw<;oN`fyTmyN$_#H+ynEDA^yF35%#i&`7zU}uYcgWQ}O6U!-;-I{h19yqk%9@;2QvO3ePNiW zbA~O2TK8(ng5iuf45VpIR5~P}P(AZfKOs4Omi*3X7a_i@)+}DL7zy4ixJ8m+NAB!G zHk=~3Jr2Ri!*S#R^gCv494!#j5PUdY%nXTN$ptUyGtn6Y7%3=vp!ZdKc6Yh^*Cw?9%@&Q}^t!_Sn3l+-m}Qf57$!&0f&1Xh!;EkDEB2&$|PS zZC=#x`H?6=o0k;x)AnqI_KPMwZJ?6aR-~dpbS@Foy1BNSD0K*FL%eZ~l_D)b5^4^B ze&A$lc{X`<`Qb2>6|tToX^*?C3rq&;hU?8LTlRhKw_za9MBjab<@{5}sgE>>bv% zAoYa+SC?<`BY3d{316zL?4yFwHEM-B9{4XmpNs}pnYKR?+_?t1N~qB80TFS`fG;v` zo-^c4$yd6)t@g`E!zt+bl@tGMm^Oz{8(c{}c<^KE8^&95*s|B>6VA_pRKMo;_Zzdf z%r}N}HxzF`*FkMse3d?}^6V`Z=0qJ2|-wHg;?zUv~>9V%N z^5OE4%2y3W19x$LVX$g&UL%+94(QFq7U+~HNrVyr`}?j4f`R;jXY4{(D7~@zxeXqV8Aes|ZR2cP#>F(N}>1bDZ zent^aIR{cU0$kts5Ba~(e%NV~eEgWsaeYwy@uj#Lyq`VU(Vg3Xy_Z2!Ihc;jI$snK zF!uxl*Wi3{vx4N{w6sBZOV~nNKzE9;pUPR3fT*ia76nv}MU+V|%{jKP-`R0vEWh|> z2J`3A@NPB*U?D4;vxq4 zO$Yj*dx_5A5rUfy$q72IZPxk~*#gA~y~y+Wf)47}occQb`$gIike7_~J?Zv0LS?KH z55dos_y*{?nF_lCrMp!ggK6TZy{OU&1vn+hV7Vg(LcW*wxV;wY@4{);8TZ@_t*ekk&E21p49s}%HM=_h_jfmj*v z1aAQE91Dql3-ao)i}Yd}{K1@TJ^We(4p|*Jf=VGAY<0AKrT$zM z-wOvaQTK;-Hp&O}RK~Fgd|LH##D$ZL@MG4&`Ws@{6?)RaiR2X@=SSVPDYYW(uq!-M zl&3zSZ|(CtSx6|f!08u`PoQ>A&#R+` zU16g2?c--j$(LBGmBvX&?ioB6*)X((K(;aIa^^g#;ebqsg6Da- z;oe0Rl!KPcOgseayF16RlnS2QbYW!~kw0eyl;r9W)1&hplf?F8%nI{mB2irU_gDU{ zz~F-<(@c`}n3dr=XNZYde3CO>&t+%HTis#+yH8`{K_Q# z(*)>rJ$YDJBl&YdI%{W*)2hA%UU-RXL>x8gNCI&RyYEIOmDanb@#LXnJ0sWaf+SIO zjO|jx7$F^gPv{HmPI=^`JJuuK=@Uq#l&0AJ_ig{TLY)wD{ne8;sHdEy#7OXDQ1WG zJK~WQZZ@_qLm`3~eUj|q(MRBRaUaQj;d5BlZD{OmLUaZ*HH+BpipOS7| zIGPa&WWguy5Xl78_id{54Gj_Avdk@t?yFZSw{8RI5~(*Fo>B26%=`l{SJM?JdqsZ zG`U2d42uh4JncTM-d~XK_=*C>6oP~Q`yUW!DX3#Ty#XaR_IOPBSFzGfb zifp6MFMNd9xkFgp?< z$oz61afdzJmVY15Ut_HFyjfCxNM$(F>^J}H^Ne0dedyWcp#9?SPB)iUNN{L)IP=xN z{OUa8P~%bJ(?5;&e?CRX0_ALRX-T=vy-VUZM=eCEjAB&XJO$Rk+dzXBckpdF1M4?; z#2^@MfaJ_>;A9i{Y-rPPtk&TQ!qZ!mi1rJJp4yTwpUg+mnSvT>OTNp;zYg0U&tNkx zZV)e+P-yuLa{R0=Jrb-gjFofbDnS~qHXzo5_9(-$quS$tKCs|=;32r1lkG+08Rq=? ze)`WD!a+K*f@G!Ei#R4g{P9*kb5kiy%nK4+3MVpq8Nh~+`svne!xX6JTB_^0WP&uf zIUqMO1<5Ow;J?~Hu%Xb=|N93)Nv1iw6KPOctIXsB8X->)s6U!IY>rgSc0%am)HI0aQYkpIn3etpctkFEStVe zvL6TP*X~Go+v1$>pvPFZzM7yj3t*As%6`eEHci)~z@OmWdZPto^{4=B8$YF%t3>JM zKEHGVLXEV6_Dgitl=3&%9*hib&`?2^dFlwDrG6`r>?fv9><5;aSFLBr*4LmK_zFCV zMcV6t;IiId=XL7|LSGVY0g@xERs0I9XlM*U$dKJRa{alc#&2#4OesU3>hi%ACHQ8M zwD)uG=G+EVYPGALMd^tT_5b^oKYd3b4Q6%OXo_vy zS~dXHX_brp;(BZ`b^%Lxq8flWzL2W5=T3rTd6k{{_3Qy?(mS%m0tk@6IP(O_00t!xfuhY|qstIoTJGax*qX3zBo9XI))0(S$90 z;2^YQpG>IKpToXwtO)2kaNJk!+UgUI@0rb=( z-m-P!pLZ@ilr=#}M?wH3>!M6JjOoXODTLXJ{$^g0N{6gl+r4GqkOEPz^Y>AVv7!@ z0cq&{HmDe{`V<9ZywLT zbc}g(AoE-fifIe6SXV1i5~qJ&3Kixm3Lc_L@h&bB)(xqplK#`}MCvU>0}ruDRnZ89 zgP5xc&B%^?3-uS$!W^i`u?+v~Yr!A8W1uWPfX8r@%v5aZIx zO)aCumS-nv_O*ZdcnaZWd=ipm|1Bf&-wYjicWFS8>c6Fl5&vekhlhsK%*#qiPru#z z(mO!3b$lQq(7sUvHzixZFW~i^w zrRcNzCf}v~X)-4uRcqJ0Fn)Iq(!-hs&gT8twe{YqzW4pz%0oV1NJs0Z#RaH;PXPP% zcC@y2$sytAT}1OArEje!>c7cEGA!uFOuzli>qu1FF%Ur=jvw@11@EU%`3ocwd&pAz)RjM zR{ADmP2v!v8ZT(m7)7Vs1s**Ueb4>O?jhhU`8EGGI`6*#q>m`$-uRK#o}`!O2PM(gZ@`B$iSO& zVEo{CH(ig&ct|kG+VrzS^h$5Un8QO@1k80EqkhTstm`4;<=IlGuEAEv0{O`zD)VFj z8lg^-pyRb62s5c7mkb?mY&xy+ju-gK&v8b3xm`6^0Ko5+YyJEuJwyGm8<`mxFa44p z8Np;?c_VqXc#i;A)1HJ=G-}OpQ4oqLWe+cWM zKt=2HtyIiZ-8hiTD_!nI>vSvPKH_yOP%TvA=zZ@*koU3R&-0wm5iO=uZGIt>w2b&W zykLze;P0El5WF%7MRV@~Ev$`$(R}s(5Aao2*(+)d5)M`Jx(TjFTMJtLIV)Pg*lPud z2}K+swMF1Y8SJBR56}f8c(Zi8IoCftC?{PwLHzUB`}$}|VYK@7%LKrXqt9RS;Ab=x z;){`7p?OQ+Z?y~?2{s6d&ar1oRiSoWUs1z}7U9*ff!Ai%ZsT>|{qiv*rrapct9?l; zY;UpuumI?lC=U3UMqdi@E^6kh=0|f_O|p;bDzVR*+6$BcX5!Qbt{F36i*+>K6liqp z)uLBS5qd{VuVP4Rz8+P~`nOrq0_p4eo%Qu+vxpUDjFiKZ>CXF$Hz^4l?v8O~08>3( zlo^QctgJrJY^`A$CD1+R$~f)fDqPicbJ2Zsb*M1*`OR*UmsWkDe$_0(aeaz*HLFG_ zm~qvGbUR1nuD8niD@_9gUjhirp?am>wQCj$CBnk+yGn&Qfufay+G2XC?4n4EhuI}G zBO+D6PgdlXx5Fg^+?%D~RbAKYdGctY(4+2Tv{F}ls@5UDf%Af6Y(wp|8W$vOEtD@Yht6x~e28BVXivQw zYPUA1S#q^^!GdkK_IeXF%*}0jbN8g!9dPeUoMv6xCnA|tCyT7y3PHcW2RGI2bw4qG zA%NWBdGXE8x^6OB&SX_s&6aZ)WNOXR(wqT%js`IKfrtg`BKgx9r{tQ^e&&XsU84i= zGj$GC_I=###4blzV-O`#44U=hAG;U`_Z2F&zt9KW{$<+rxn-W`o$=wSfcywFs1OT| z9B!bkF1ql}cs*Ip_)6x%joea;xC{Z|`o2$ST}kp$3(prm52@}je#wlPi2!h^bc70x zqlQP=S$^W_Y=gMM833U(JblyDj$i~(K^x!SZ%t(vTO#MD#MEZwTc-J1q9}qe$!;xn zn+1pifxcWB%jwp{kU-K&3E`OB%T9A_LWeu5>W&Wf>LBniT-R&n_+*=F4q(?tU|;}8 z&4g|pOy+96*+Udu`f)!DfuD_}KPM~lX{&MR#Pf<9&0+;SYEhNU=k&dCY zVaw?9SDFw9Uu!au9~&WM|ItBCg76a9A>NrT>q0^uDS7|&vnz) z?x_q8jTS$0qMZrW4kOl+s}0h+;$v6@)?lJ)@t7l?oL30^Q9$xyhH^x^j*8zP(b8l& zEphYbi3J`iFVdVHd{^F7Y44e?2%sL$f7e7U>h`%toDQ5~3rRL7Xs7vNoEUv9Tyqxp z&Ce<1{2ZhhZMD&o)J9W*4tHf=m4p1Su8gtGY$^BZK}Qg{ND4wNxReOSFQiejJoZ=c zE3L12cs;kOQOnI2y+QAF3P(Y}uubw_74S|^fbGmW725+r!}{*qjU4su-m2L*fr_NE zj9U_XysiAjD!JhwTs+>RBGDRf?UnfKg!locXDuuBv)Flvg9SG}6D{eRu^FctxU|~w z)7`OK{{q45Gv%)rbZ0pkZbjj~iWW1Oqiz?A!qSY;sQUVyP7p8pvX)=)z-7$Aao;IK z*Xicscw>s!rU@su*I_OBiG6?v%gvp-t9RYHvh2MEh0PN{=(FLgo!oytJ?ptX>kJ>bhYE!#sE4&aB%m|Je0Ogo0qnT9R8Io@dE`#QU@M z8G8EZB~<1{0}JV#qsiE@Y0KpbU-T-B&A%=fG!Pb#$bP=b?q+K0VGl?!>8!gt=q`Mb z`7WB@&2h|CxKQF~p?KjOO*bSQ}mU?%-uYWi>#$s;oGcXQf?L6aDzY zJKI_s(0#x7BB0i?9;G?}chAiM`^WI_DuY`d1h;Y#j6F&GQy?*A{d*7!Q~xF08nZV7 z6!YmeWcE^N_gW*omfr#!Kl7U`UY%sG#WY|NR24*jk+B>tdL0U8jW_xzG+#1;wIYu) zg{m{6HEVBk-s%7o{ME#8rfy4?nRH7%_^E0&(~T2?*t8M#Q|;dQTH6JYs)X+)Fsc&a zn%(G@=ICwejdj2<*d)gH(W}%RbkP?AVHcBgwq~y2)t;!^*+Ss8u49G5e9wStz8Xh` zopL^hL-z>3+9pUTSb8^hT?L?mMYu^?_tc7%CJHegb<67y`pXcsDZ@)&Cn?JEQC{DW zlKo|YVJ_T^Fx%>p!mWy{GB5j*j`mZQb@GO9CWa!VOr$L%B~$f0L>*TyKE+xt-dH6U zte&RDP^8U24^&IPBs`paz{QqCWm>&+u{r%f3{;rP_-YO{KWxQagz@OKr}9?*@s zKJzJ`zhBhYl}$PV)G%AMww~7`V<*cg)QqMd6N^wcKw^(v;kxy3*6%YK?M$;Aw`TsyDx{Apz$z#GEp! zxp9(f9SnjjD-bXFel|7bjO6>?pnM6GAsFh-^iO>Z1o9U+r8(jSS{`T*dA?QzDRT`1 z@Y3f`%(ibK#uibTOod?TM|GJ)?jfl;V#?y4*4ujl)*k?&rpRb}T>h1Dim zU6gFmlJ8culdH8>L3-GA&wU!uH_Y({@{4M_nzY*BXrlf<&fYu_%D;ObFGZo!N-0YV zk}V-3OKGu_-B`0E#+H2-?MW&jyJTnVV;Os84_U`H$QpyOPnI#iGd-X8^VIj_^ZfDq zuMFbIx_H>pHx5F10!A*xI*~KJx1Iy0%{)%uUV6n8Ke?ezgV}nkDKob3X&8 zKW8L1Fkk1OrWUb!T^p`ZrEXUC_V4hg!ErE!;YCVmjsARt=g!>WmBFRqhfzgR-M3@x z=LHuhnohb9uX0)`oqU8-rKLS^`_lE+*2!` z{|ru;SYxn#!}P;!&{+&V%gN&Y@kh*)-43|>&-R@dnFc~7?#4J$ouxbsFq{JL6uW7Q z4!>_1`$xSFOX|?>ymJNafjSwYhhb|URFN@c0Ct}#IE6dxeCe>JQsdcN)?0RS$(&6A z$MC4`M4)ym=chZ^P73ID@Pj%J3ILI{QWcu;q>W6Bb~@h7K|NxcX3C{>-=J?^>)9@% zdDHT$M!2%_!H?Sa8`h|9=W8&XSF7T=oLuJ+#+)qkW$)t)_Tc3+xALO%v%W`Xx?mc2 zQdUDEYxw5>oHPgZWR4(?@gX{Wb}~=dKdev6p@8mku`5Px^K5DXv zv7qs?dfF~0kE;HR-m5WxK}0=qos;Np?Zen|hTGy9-MgFp($Psz5AJ2XRmH;-_7K7A zQMTxQ<1Zw{^w7NEcV}Luv1@;Q`G58g#US@U(XEFi#~Mal?d7M z9~|hTz{KOWBzS=NfralJxS?S6)9WaHE8Eq!}^Z;$Cq=Rfnva+)DIarQtRMruQd~N0a9N$MM-HJ2G zJG|D#gsdZCcXyU+^5LBM+7aTC<&K?yG>rT;cn-Q*Gcawmfjzwy*u}V^5CvG^MB5Gy zcobSRF*b#W2E&}aeh8SS@?_8-qAq#7MB8+=zdXu4vY6`fO~=^RLc(pXz_cc?#6;hp zirNNnm(}J3`qm(sh_!;17M-1RO$Fp5Z~J@WD>{|J@Izrd2L z9EDJxUZ{I4&c?A>#Wk<-c4qO*6@lYXxax`SEJLJhEVHTK>_!s>oMVyj{KlyqIkrjy zzRPT&K*fBx5ORXoixh;V)a|Z=9+SObMKgmE9dhhvI3;GH%HY*EI|Kk2_T~ztHOqu1 zs%^JHEZ}#vWmfN=LBZ7w#B4j}rP)y{w$kbjBsR?!#!I>JwZI_uANPVw;Q&?MnFpZB zcV=N56OW}^b`LItDLm7S(zS`G@}&c7sSgKX2o}mL<#l)JowMO7j-id+z~7;i0?O4@ zQ0w$*=@^ewITaMsHqGjhDZBXEMthTbT5DqPtv$2I6A8rllv zdnar`CEBQSo^a2AMch1<`h|SA_9@b&%wMb5-3WtdZOAPz4 zEQ7Q;{e(acq+5MJH8O?PnX_3Hb$HF+(f3~mkN-EmH}~#|1T9}W{}Vkusr4eyov*!s zPrY5&HgeNsDXba5sTDar)QuO1?VjqO9eBV5sJ<-H2kjEoJ!c8yEiNOwd8zDAC+WWU zEA9M!?3vs7KRzBJ&`fX8x2aA!9iUQZR{k)*)EH+w@hT!~BQ3z9?HwYsIYOwe*(ALT z^23aiO?>W^u(B`_hlijr>(4cNM*o?c@Rjz+?Dvndg#b2h%#{)bJZHVe>JO1K)2b~N zPW^d2{}Te7=o2HdlHVFVh2po5h3xjs>fK}2WIxjf4<0~z#8w$96VRO8Oo< zoeDJ|0-t0Ctgf!gvmo-LV{O!Pl+6li-kllm3eu+>d)qYTKGPzxAmVG$36@pA^+YJs(*j`Kk@DNZK&&3g~#3#R{r6X zBDU>~sVhIM(ob(wzqrlWA#;y$zNV(eVq$4>;IA6|pKHk-+c^Z8vPO~#-L`!bb|Ly- zht=;%`L(e8^CN#g8-LA1CfXgEcazk%xi$Z9{KJ94!L2hicUz4wKRdv3>DA}Mxo{l+ zugA;&N@gI}tnRao_(}$Fmo109mx3sb36?VGo1lQ%sQvN!lcU++z}b(o3AP-;z|ran zJyYS+NznqZYARlV6d-43@PV9XtiJUM;rz3$|Ho0<_B;H(2%CIw69-t>e!qLgvje#u zAY}UC>pDANarS9&4j8H>xwEX!;gDdZRtN)RFvGC{-asZSIIX%rmk{m~H&XXW=70Yc zkg;(b1m%>r1WqW*7t-d-aQfq6K8VQu9`-h#ct!{NvaP2vF5PGR|!B;B^+AlF46A=?l)A^vJM!?X% z>tVC52Y#svl>vdIdzDl!U}8IR(?!}4%*T)sI*IoWl@15cXF^|hOQ}zaxKvW;gjL}& z$}HAO)vfMK&$+%v7Y4*~KPkrl^;TgSUn;D!iZi$i9pgeTqFsiTkT<=MZU53F>juPX z+>JdT^>Xm%BP87!R>p%yXb8lpIinh9Yd6W!=r$^!IG;V#+$=M9adGtkChV0`T)Ic4uAGHKX$8l4WneLsSdcb4ocpAP;N_2&$us&%l%P+ zQComrUkVI$=08udE!9VRhWaf3xLpValBFL<_rG}ADdtY;;fUA|UTyPap#&Pc=(2>G zmwi9JJhO%V2zY%;Z2%eFK(r^|iD|Z5!e@_w{~dtX-`G*BY>d${v+=>Gt3qgnX8@m7 zPREwc)t#`?L#{kfZf3nAI6J6=q~th_`(_s;1%AIn4iQjeg7x z4}!3<+Gj|2U)httdSrO?(i7o6aN-8o>K3ulS2rvcc5inWjM9INnLD9;j2#Ih37ey@ zbO35S!g+JA`4IG(nxiCIBa80aMQ+N3@Z29L^znNVx8t+}Igx+zTWAN`2o3_>1VfRf zG(>KigcT0sLh9GfM$96*K`T@TKB^|5V#$S!SI?kOjFR|gWJn8bn_ooSVps7Opw3sZ zw1zeT3V1nBuZnyH@`c$r&wID7HglRd@5LA3zukwzDs&Mu^IP(61~TeF1Hr%q56E$> z^GeqRK48l5_pIW>jBOXk8ni*d#iZ2lXX2OO*wyR|{;IAjy-owV_KM-WXfTdlFO9pP zn-XRZN|t1BcxXo_Y7jdxF4MK0vm$NP?%TesEb8_f!^(U;tUpN)kt@j$`);%OjH!Y9 zksS(w&I>~!Fx_^s%PXP=_LMS)L3%mIYM=<51}P21vSxYDPToX?bXh1}P;F~LnOb=0 zyQg=>Aoi-n3il3rY_KrtmL~RcbSGHQ>(m-=Y&l*lS+b6?gt|jeGEUS!7NntdtbQX8 z!k_)!1Noo#dg+-=*yG%$5fN8CgaRFc+sdFw&WgN6BEM>7$SRDt2!kPD9%zC_Y%wFgFq&bCj4 zA9kj%9l1iq#qEG0Ka^;&&OiBg6qVsTDi`8kw7f1}P?8xGqc@^NnOzKRXN#v>mu~4Zhz-f_G7z$T1L{P9^LMsdbq=1H0$5r`gg+eFD3?E&Hr)12z|&`CVWX(ytWH)nuq6s z0U#3kGFf+SwmS-P$oagWcOL^u37z4+$_jHcgH9=Jv$I5^4%S(l9`^?N-=9mBubJ%y&$U`!810 zzjF^xo#Fp;HB+Adf4lD9GK)EET>iFvE{>hX*AhJlCaJ|XGVvRz>X zTktStVFV;?x2B<-1e)qM(z?^?c26wr+(8P(^&RDB#Vn|xIf{lRQSY=?2A^B(fOd(a zMTXkP01HU5ZsO-*NB|;E%4gF;ioCg`4b$AoOUsy?`)z0Zx&j&btf4wul2&c}4C7*K zCbMGf!ZRx}wX=Q86K7P6{#x(sUueHV;Z*}HSD}Fy53U0fXo1=lrkVurcmD>^`==u+ zP{d}sP9=r+Rr&8iX-=tg*+@qm14(!hCF9yMBoc-Q#wxA!SeN1!vX_haQcwTzF@)aP$4_^OKKcI1j1q$egNCmZibnaG2>4 zq!2eK{d~?>ac@3qc}jophxtVIW=1DNW|rRfze#y|t|Yozb^q!f+`BR%iNCko2~BqX zN3l#PPj*n9&>e=_xck#>1`Q?+0ZKBPu7$eSx7x87H-qJ?#3+Mv)=MEa`K!C|-eK$`C z{rtRmtVv`1oA0k5dp6wr>Rf9=b#CzbEhaWV2jY1|&)=Odb9H~Q{fQ_+A4-&x@jTR1 zW|3c+-i9z=6MCxmefNH8(sBqN*{UQ>-+KH)C<+kLF0#N??NK%s4d`#Y&-+8qkAWod>YhvlDl zC~RjZWAGXaJ+Jq9trMk>g-(+%bF1naJv{5>`Pt09ht(g)lVKC8AlS=AXYoC>>(t z)je32k){I4IC_v_)wM|^9h)<(rzO2McBCUX9EORSi{hL^h{DNr{dSejvEC!{8YWG> zuUU}LsdcLN8S=&3_UBmy_JcQBj6@WMbW1@ooF5%4=}AciRqC}1ZS+ox+oZ<$ZLPg1 zwCe5yUUv-&Pge8G(EXSIW67~&$37hWaBA(d91_g5hpoXpJ8rf&zYc}(F4wQG>L1<3 zMvMz=5xqE{ygl`t3@amv1uu-67a}bpDV#{g}xK0MH** zm58(|YbsOU1)8=wP~qk{b>s>JzlKPYH#@S5H|b7_SihJ431#YKzc5XFQj%Rcm&?KCW>}lLcAhD2Z?{Lu>1GnBi3m1hQr9-1dICkD6s1Wr_OX7c1a2w zdKrzlzy!S)>hEQAQp6djRiF-gsgi;>R%i2>jNGfqW$WWN&XxW)x;nG7fTeVC~g zO?FNB>Q*jRzt0swb9gU)Zv4+>BSUj|-h*wg(Dy43F2KsEriLWZ?Ro|W`e7+ZtY^y* zlO>E={)YUIt_Yu~0&9~QZI~0#Q(GogtBo?U;PErEV)I@8m^L2zj>?McHR@+~rO$6# zs&~VGkWz7nYZ)Gtj#)_K(Xhzi&;}YMt`KYF)!gaRu1J@#g70sw%${o^trPRXhoE=(-;z=lq`kKQ|RYZjj#c^{ld#!3R?r!;Yorq4z-nh-1HPu*N}zAHC{B` zJ{1_KR0u1xq-)yRW75!##!`OS8_XgoGZaEX+R<1ee2cQsck5`(9MRbg-}OWr)pVVD zH5fr+Vs->EX6RcpZf8tNAsKBEnzkkz3_q;5X=2MY$IthyLSMF2&k38Q zkAKZK3Z2C+{<8VcW2`Spe`en7yXbzEFP_9v`B>0oC3~qKq6~9Z?imFREN_SKJzxqqpB8D2pD5 z2){l0$;#$32RgZQA`+=|O^AcKuPiIXQ)-U%pwMx=GCu@5G3|30Jiq8%1R~zbARlO- z{dt6Ny(e_X3IeUJqK5_BULNC{=(Z~NrF!IOmvWrkp=(W7;bGO8@#!%7j7AmT#p5aL zADA?kS807uLAmUQywQd1>G+~FnVjg;g(?Erd~OZa**y(T)dx-0!Y2l#xz!Rj>1+jKN z-5(V-kW;&@DV&m4x0#;1ewz!={Pc^TRAoJ99+U3eu?2m!Wx^b~xZ@=-n%1%5072ZL z=O;gX`~(WHJyObD7C~573I=ahY?pMFJgc3~pNvRJe5g|_IBf9rG5P5IeW#Y@!u0KF zue`V}#+H3bG98~~6gS*C(|&JDD_3cLxb>99HN(5*1uzd}1B^>ZxRCOdtAD4KKXwmf zCKq_p*M0GXj#d+uwM%EN*=^#dtXD7*tpJka(g^-?Py7DDW(uej>pNjp1jamCgPcqU zG3)6Wq}E+bRF7;U?+Ce9k*mrO2Y7Xw!Y^-Ft~OVB*Nu{6zTFafHa(c(q+8?~BOP^5 zYQM-rfbU~&T=%jdje6}j}bk; z@$*aYW^4JAs=1^~stSuRp8K}hRFWoUK7n4D?_j9#V80}ekb7G8DFte`(lK^IYi0Y6 z>`UWzv1$CXpX6y zrD5psw|DlYJiTgRohC>*HoWV1V?s=e3jB|YuEaIx2B-MU97wC` zhI~jFRjd%oKhCNAokm$VKDXu!N02mQI?`+yBy&j*n#xR~Fh(>7ezASP>T5G8Qx7hG z68ut@73D{een4}_K2C#_Ivh{+!Ogk+3k$+*lDe=qBxdo3BET*x?=9-TpWdS7J#4$z z9mwUO4=q#N?(mU27nP4FfJagt+jlKg#nw6!|GnZ-l-wuXT+@`Igj0V3;_cDUw!hIf z&DUJcuelcl1>9;`Hdp*;BY9=6i6()K!8(>)G3y4A^DWuKkx?^?$sMo{nRks{6_MAp ztePhH8q#jPBmkopnW2&Lnolq9T$|FT$j8_FP30r^Ugzs6ySqQnvMKglobKyG+^Qxy z^S>7SN;huxak=Y5mZU6A_on5S^V29=Y*d7H_6DW(lT}Kf8w{HKv!-~{K<%Adpj|(9 zD_RQDQbB(TVl^b?b0jE((cmWf#^O8C($rWTuoBXr#az7*|Nb!eCMj&0QmTn_K|+kj z_Zfg2l2XW(Ukt8vjO!}|x6gOhmuHSqM#^+Ty zDN1=WB>t`G)5GZLX>|Hh_he&ClYW585rWOP38Y<>lIWa<{rG1)(>WD+@~KdAVis%_a}%7n^*9+NI4_hdxbJiu9FD zbE%Qmmsh_q)1@+{H0Ze0XIu5a^AOemy3W*XGcB}Q;*1jVdQHjfY;_K{L|JL!E@zj{ zi9@;_-GAAl@hCPj8{FBFtNLITrws`G?4qdr3Bg`NeE7<|$@ge9vKuo=aVmG28|Zy~ zO3IYZRGDm!nex^+Be^ku!e>_ge}HKT6Q-DUZUqsMWw%1lrC5+`WaA%MNiDueS#Zz!a%3O1Mz_ zl5xh2q4)|R(q?`u$>PG^6zt2tQUBfai9uGQ0m$E7UFbQ1rq)oj&A>*?d4$6?=|Y^9`_`Gx1t%;8GTfx}YlxwqRAfc6vwHoTodvt-m+-=K@ch|NO5x z2?9pjDYD!N(){w~t=rTOIt>LcN-odK7Q*OJEJgtj81R%Y)zbMYC(?fGo~%500KHH- zjd!O}=LdKhwP!{V2b_Y5!g|5bdT{fv89RDur1wzw=pd`zb*j*O2yDb72FaY-_dCTb zY6mrX!wr2SqI^2>zL^ha3(`6^pP>v&@}ita^RL9MVXdyAaAgl;sFI;lQYyfCwZp7; zjT3*jG}(2ln92euCxZ-vsd)6Y#7v+-WsV)LR1YQv9@f$kdPS+zV%-;Mql7))QMaZ} zS!idVAVn;RSDLqZ4;f7!kWczvMF>%Xi&yClA5Huv>gCQe$~d7V5N+I#%Wn~H+FwOp z8)!9TKkr}$(!<$Vx4}aHU>({k&|-b14}JbM<+ z-C_ETN3#YZ*z#@+>mTSbF4HeUKj{TwCmT`3iA14Vgq`42AWTk5BY@R|d+xijWVIcYL6_ z;V*QzHqWp4YNg#;7c|aVfxco2P00h{vyWG-t5fE(=myiI{QZlMT;j_7u4@4j3da@g zhXRN#HH+wJmv!7yQ|)8H<-8A8RFA9Tb*;N@V)HOzAQ!3wtxII$7kBLQRX(ls_a`U$ zoKQe3rb;D_(yS)-R8h5(oaR_iUG>^#P{fjgu z9?<=#%`R62ctQKD;_aG4^=ed7XUHd}SH{f$6NCTrulDD_x)NTzuD1V=5dF``{;xQy z#^_GqSH^7q`O=?}y#4t@RWs55zkP68+U+&Npo;yCVrmr2z5JWm|L=Ra_up`A>;D_a zHdHR5`S&mR^Y4|T-ie7!rn;~_PFeLn*O6%Z@?ZJ|fQ?%WVq=AFv*YNLBE=dw zRb!)5w*!T$BAWSG8o8F&n@T&<%SD4-EHwKv!-x4!3Ukv|Kn z*JHni$Xk|B?-i1FFJB{L4q$t|%!cw!YhLPAc&}~tUm6X#YF9q4Edf#LYe%q%@z?8z zFM|hYC$$9hh*|iMd4*necXsfj)fFNVHA!VoD(SN?LwRf<)fCh*17X1`M6TLsY0X*+ zkOD-6JWv`*c1|dv=gf_Zdk#^ktbp!R%A5+}NhCA96_R1k zr}rk4h^qir_koALX%|wnb>;UZ%{C)LIOuX zCSw-@B>PV&fKqj@G9M~HpB0Q5szMJwts* zS}OTQCRg4+;O`;Jhd&OWoM_jOgposqTwPC@m6HPi%`vj6$hNp2WC0ktcCqc*SKU^_ z(u@{Q0akJ6^ToRTm40N}?5IX4)8a@3YJ0AjPmOJ6ChEQ}WX*Pb;-rFjR+d1yfNT;Z z6vg(O?sDz=!Xu!0!XNK^6>Z+;#WR3U^h{SV@+l{sEx%>NigsLC4T*X^%7L5I z#5xeraxNWf9xMCZSn$3~GpQS)bCv$`7E_y=mAdNcb3>lEl?sL~TfsD16~5)O{=sc?T8;-+SZR5`=pXR*S&`t^WO>ikDX9Wa@j9sjB;*^& z)I_H)0*nRC(+hG!M!&haFWb6Y?Hxwbs|9D6zo*JH*WEr8&^{X@5}_qI%T|vir&m zRE0>kHoA}2PzXjdSSd4$UO6qZ(m9+mvyRUaRlf8|CaPAWZELy^@yT;PzAq_*9Q=S~ zxME{2--}Qp`u0^VI^8(ix85$MXvnn`@W_fAYo!B&H3+oLIQd{k(AitBat_Q&6nn47 zw_&u9NVQ6Tijp;pqY4aAahFLHiP)-4_MS}RN?%vsFH5s8o3e9&CeQ~q#s;i0A+J9s zdQN%6_=%sfd$yRk;}A_#zmx^+rl%1bWqrv@N(wKvpGsYnTv`^=E>X8AotQ>uf@xeg zbYpI?iAjU5LZYa)qn8 zUsEviEk{auY`NK?Dy?@eL~cAiCnKw@v9?^g#rYe(K=Joj_f1;2eJC6QU|0eBwA|QQ=c-RMJi{^yb)3^GMPpdLP+s?iL)#Ax#8jTB49f*qfn^ zS_78_)K1eI60NTt{-7z)rE1mRZ96Bf3li1RtYzP*OKV@}3^13OV}|M}8b#P&C5E0; zN&LlWmyUjI_eru2O}8t}QY|M0JeqFAC&5xF!RIRDFVCHVVH{S|rV|{vN5YqlK~0Go z-OqxWC=tQ+=+NE&Njfwwvbeg!nxY)@8($pVXpke8Q($mk(uxkUt?Y?JR-?L(bF0$E ztqgtM?C}~VLmLX*n0;aHLDj>ZbGn`h@ek*6G4{B&l>-@C`C(A4ow>86rfOQ^#}mJ} z=x6Mj{cMg4l$XM5(}A=C94lKWQUcCClq(E*Ix;&TKy+*JmYdH{G!|dzc=MNC^4mGB zw!yRMP7dw(jMJx@Y^rYAZ!7lQ zVJ)|G5m0}ZQv0%#Fbs;|k`1T%4_Ub%xWr99hft`-<%AU}2WeWu5rK=dxFf>8nR7`P zW0&4sb!r2Q02jX z3pot!=nzvgE&%Fys2C(S-|YAHj?ZX$&ydoyNxekT&AmuZVTfJdB|RVPhTeBVl>fAv zBc5+gmVA~$dPJ(HWzFU_sbG_9W28yI2Dk3gjo&hQ(WuaPg!&<%7*6Cp20ov*pdc^S zcJlZ4j}W?th-%tOTly@(hydk|&3G%1?ZUg7?)lB9-GHILpmL!LfQdt{_V403er0}fyI7h11e&Sp2ST&^7*FG2LcIoI)V~&oMp_fp}3ysSfD1=CnGFzT3M|m;Ev3cwCGk{P|_#-HNFdTl>}crOo$b+8|e8Qaf1h&(FDCI z%dT{td{s+tYZBWu0aZxp;@A-!OG#zZmK*Ntby+U&15gTWr#7EF)>BiP1!Ze*uSH8C zo9|{dX~2t6KIch{KEoo5yPl#FWerHOTXxBy^bxMDBdI~-hatfGY7xh0=49VVeUz5evN}{$ns=`` z*236t1x>#>*gtCIwqJ@OhdqX)zg(*>%EHB+ZIS9}#VfFXPrLxFv+We=gd*{??%WV2 zTzcu7P%+Kg<8~>d=|UGW+Y8hTPB`>x^{-6R zkoJGP02l_!tGyWZV1)+CFI*uU@;T&a+mDQi$toWxanuKt^azXiFIJ{mqoFbnC(*fS zc;exy(M3ZZ{S;UO;5g+1Bh}gpiSAnfPXQYp;}wS|jdQe~jVuWt^6K=ZkkmhZd5Rel zU7BchG7h5{C^e1v)=e|Bk^1h3-zNTSo$wC(NucqYc>E+$_jaDosGq$qI3l6I4q}do zo~vGO8#=SjlH^sUo>|vXa!Wio#D@)|FwaIQE2`w_e^FH~T%A(yIRxpEm@<_$cL+=m zc3T!|5&J@^Z$WD-r+Jdvba^Lxj>yvrQg_h=1rf&W5qX{~^-VKXt$LP{O_zcfJ8qAr z54(6oqDkbI!`8hvQ9B|G^y`74(w0mH4*nZE(nIO?ru7E~I4eM(HN|DNuPf#eW-bjK zjA;S6WwR~TZ+%z!h9r&;yg-wycJSrp4!hKHbxEn?g9&tPU2dkk+ywdqX>{Nxx*h3-HFS$;oph3> z5dA&SQkv>KFHx1YE~uR%4Nu#Xngv|OFQldt9j!@Cx*e2e|iasd>Hm3D$GYt^E(dOxPfK2cUdKzOu`Rt zU2uoKlF~{SG7YbR?Ih#Y{8kqVf@N9Vz9G$&i0=Wk>InU~=v=8+>WZQ-PDLip$pbCU z8Jj`(_Q{;;pkpew-ge(YTx0i2Cb4rZsKt#aj*izd)Uq#cVP?|cM&QauLKYF88@r_% zHHZT~e&h~j4VP3Hs9+NIrm1ais83!9VeHe~)m?VeiHI0)iRquK*svp)OPW^;X1#K* z)H(!7-u;T{#T1kJP#q&HcsEYy_AAEF$J!z~IB0#rWxQKrZNp~>pq%5{NlUNErC%8R zip1IovBO>l1zzQi?LJ;GP;qO=yTLD!5$oVgLMO#XG~lxrsnE+;j4GM*v^VscMceh3 z5^oDJ@at|!3T>W1_U0uCNlr#P8Dta0z0;BceUB3z-SE+u-yEww1baDw_PsZ-uqW%u!Tg(|N$! zgeW|WIPxv>N)vUfa9CjYa`P%AB2xj56rCjMwQx%if`^+s7~U+8%1C8bhIXkqeitTu z`ScB(-Pl1z1%(sL1O-&2@6c*#MkVp1!A@H0wB?WIx)Z~k(u=UR*nU+rc@LqF)4SI4 zTcz+)^;5DxxZ-?AYL722KJ|Q+WH74(d5&6XT#iaM7R1y?Pz;lA&TBm9q6p#H-gjNP zgb5Olt6O!$crrUpVxcpEz z;dcLa(^kj9&D8%`w?vD*E1*WT3T5{McC!P_AcKb^S)qzG+`)raM>-+Xb&Ow zFv{-7YnCbWK6EgZZ)CYT?l`o`y)}mODWdK5{h`thhKUt=z`p%`{MBf;$mnFfFGg+|QXq})qr!5SGVJrSvD&l;b(Fi)=|;R7_LJdy@pq&HE~i{WJobv9)E4=z2M# zJ@w&ZJv|j>UI~szcWbe8T_F-~P(O7~z_a(E4>s$V(O%$K@nuR7=U!)Bs5PKx`r5u= zR)Wo-MrV<72|+HE_*%bCQ`z1K`};)+pR<*Aq!>Rm#-0On2ZV( zBMnU_&lSjNs1w!E(+FtKc+HVg7@jLkTPG&&cqMu~U;piV+*W+>`_3ntL&F)RcbX9* zhD|$3KQg+VnH3e;ip%Yir|pto9HEgG=`mtiNyp)4?XWDI>TBHP%A++_nn=sx$b3?*x)n?fF<7pg=;3kQ#{Gj_-IHyn5<)d5W-i%fCRCj`uu{G9 zy;^q5P<7&X3Lc>;%Hwjv8x$yWx6Lk2m!~BXKT>G~R;ZlPHOz7FM~b8qLjz7J$os@$ zVywVxt4)(n`&?idOmFwc~~MkZ8jv{2{B&VFJ<)3E|&uyR!INnojYo&hY$|cQqWSZT2HKX~HI@ubjgjY(FV|75 zZB3*lYKvW)c%+&AcRz{$1+)F_G%#nf)0W-8mAMnYQs-Tuw{}4*eGD^me}{Xs*c1o) zv4)#YR|>R7>{j3f8PIduU$mf>7CwMb55eBN4{qxG&l$7mjZ(ec1|B>^R)6d@6)B9a zWn%_$CH-+{!k}bw`d9U*Qkoa;r)hJqOD3LB>KgAN?EnrdKl>&WaCp_o7|#T^ZrhRZNZN5G6)2 zsuO~dvIUp)3$3RDl&24;nS@|er@U7py;O2feey0@Y*8?yo$gajLkc2DvbNX{0iNZW zOyw#@>P@0Sq-C&^FYQjSUQ5>73wE1dcPdOjO*e@c)X)3Q`!KRu9s5B2U6!(o{3=ys zrOjrIvhK$Qq4#qH*t_Cc*S=Yx7P}(5qna=O(gkCNX{rN}nE(g!*?EAnC~G`8O9S`F zIZIqrSt?%UQEgm%BB#;8tG`6(q-qQt`dy`8&Es#dbdH}I+vZ=Mbn_TDcw;aD%I04 zP^slwaFnv-^g(|ms{QdEu8X8Vl=QiBPLUvaD*eF6IW1Uy*F%$BnB&w?1ZPR>WOb5n z2jQm6vjgF#C0L|!(38|1qxolJU2w`PUIGT~2Mob&I) zZs5`v_KVCJII62dy**uy68PziyC5?aV`)Nq=U7%n3J5=2&hThy@i#}AeH;uClcG5| zz+#b^NsZ|k^#xGh+B1KXz9_j_|HW0d?xX>+E?p}fF{v+6s)v2Lk7cAanW0w4&?S^Y zO=Rx0AHFwyYGG!tU*IXf+IVW}-cI$&ye4vl%(WbVK&TO(vycNbT5TqQehdl(WPD#N|xG38VX#5yW7dCO{k9Qb; ztEXc7cVs;nOxMgBp_%V~Rz^{@@(5IpP;mnU>vXf;5XB~pocJJUHh@v%Y-`-F6Q|pK z+v&OXQ;dN%jSMw^q_CZ-VX3pruIGD?^q3v$98w`X=!An0>Yn;p|tkH3v|z=^vU9#k`&zRrOT>)24ZRusrz zR-dkuVYiEBpTnqfI=-a+VI57eAUm1xnw$?)r%Fy3bRnAX+9x-epR7BuPWQBR*QgiW zx42(=Z$h)^Gue4PSfso-jecNH%gZ$4+lq(ZOYoC*Y`z6=1ztJd%y=@9Y<#&S!r%)F zPdiEfTIpo0UrRYB&`TZoo^&CQ1A9gXm}oC7nG=S7Opl_1UHQI_NO*sBz@pY8GqN9G ztG0G+%(m?e_I$Q??fA6Ou_mu`=67j%GO($0bNTC}l!w_=U{Xz_567W`$BnXIdCroT z;>g9hm+i|0oryLC8H}-^M^~tw`&0TzgJG%wfyzb4gpUUjBD^d-f+LHs8!;I>=fc`P zq1dHmup-|3{wV%_q4ju%_8>;|Cn2z^;eFXU$GH2T)JIlW-m>GCER<4Krg1MwiBEDE z@g4Y0h7#LJh%yZ#jr%mQ^4lYfvUB5wi%SrY?%<4Wa2n&ab+v5ip0h~~Yhy`a+;3}tz0k}mym>(u8;YRgE>2+R`uNUQI&CGG zDSB@xSEu-FN^Y~ToqE9%1KLk4WGw9Dv~I50<30iRnCTg6tAL^!$9*wTh|=9*Z`HA; z4nAijje$08*gbJ6O2jKHt>%7eA|~DLZ9w}Vp3R!9i)~Ow>8}{=b7N^R>^B8jdF&gf zVr}U`kz_NPJv7(LZ{@}+@~%Nl47((ZOfU*}FcNo*xjpQX?6`-4d8Xpx{qOPD>c6Dg zatXce!(bpI3x1^t<-LB)hOsSI$*W9{gocx6dUmcwBNMw4?Zr8S!RFK=qHt zrNyUSoh2MZ;!Ts@3#td0Z}Y3ayvJIcjYyexQ=UgNeV8;4JFCZ4qfLmZ5;*y}i=%zv z^X!o_sez4)V#c@Jvrcm!-GN2Fe^1oDCFa&b@yK{B6Kyy6KQS;B`657Uw*MKcajnqv zqs|(!W4jZ|%x-vUVq@iSUT0=c7V~v8F#TA3LjRPS^=zMG;-%$1Ia|kHzkhf5`(EDL z8^`z!#2mEVg?))QS2Um&`n=|Jr*Ad>bC=$l>6ND~e8sQcV;CRdnWxq=vdgA1yPnZw ztc<%v)W0ixsGH_^mRJpx$Z_}ZsvFsMc%5nAIMXh|I4$zhwJgzw5&{}uGK%dz7H;0H!&2TBe-0~v0*9EV*8*Qlte;j=FaWR)dGLT`n*zLXk>9_5zInB;y^S+Jh zLBS%WhJBpTXQ`~@Wl%bu7jBN`y}cAH)9YwqsIw?%`Hdv;PGzysD*BE~9amD>jzVsG zg%^~S?s>HS-No1PWsy&R+r~IhP1_|22Df|ZbT~}b0psz1lt2EjxIky}iWup5Yj5K1 zoot569ivURV*Vx_{p-8iF+1SXb)Flfe}0&9&;PWK+mmF*(&pO7x$jv0@zwu)u2NY= zMiQikKV4Yd@tWNG_}zCe0z2RCG`fHU=eL8ByRRNhdVX~0p_{vJ9K0rZGg1DK7-sMD z_NenLiH{jQ!6P_Pjf$JN71ICV!0D56_4ZOl4Rc=5gY<}DpeCRGto2R%Fn>Xf1K{Vi8X+e36*jj_i~4B?{dfug|U+L**tzh z3kA=fJ@b}YcPjCo%Da+1JB9Dfzt^>T<@_!l(yl;T^N*?!GQ7I}R?roDYxn_l2%A><3-yApwEx zCKy-%7* zmpiCBYuvff-=Cur*e(5_Mzz51YXWIumHuRS?E1#$e%hRMa{&hC(sI_Jzc!lxV>&Us zw_D!e;l_F}l$bw9ec7gm9n4h1po1GFa@D}Vz`WD)6AZ-TOx6c2Rk(e{)-7nLpA#?N z0{Mn|iXBUYxJ$YRDil=Rc75M>#D;|%d-Qs%E(?&2O@+J)XI}X7l>5`9-O3g}|4Ezm z{Wt!!@$Pc=G=0wOZ{ADP4I`1agwKmOj_+JET{-=raO>_wq05#Ckt(mi(pG-fJmg7f zOa1Mp)rNu=k;K`tX3id0XLF}s-|2MkJnu^HU)CNTer>EBuW1OkguS$%Ugl>T?J3|Z zPbM}cjdvXLiyGDqp`#uv#m0r)8p;gm(AlLVL+Dyu8xJ4M)+Nz?LAcBZl!Y6X7SaK{z%-IUTj6Se2^8-Ag}h#fcvmbmaDBi<8}J!UiK_Wb=E zckk_)@iXU)-_K9+uZaCnCEpry4^;rWuPJx5ZmhoTY2$EAo&}SvKmii<%MHzk_eg4ejJ(wXxD}IZ6`;39@a)%PGu~dGA zxvQCUc%z9SDmbdDPoUg5OmVcoetf~U2HU9F;qs%v03D#mmA&4S^)YC?`zzK$xzaOl z@nQJ4o{1BKBF&r~!viC3)=h)K#v$)Ygd&|Ree4<#!HSMHnocF_TKXQ7NtWh40vqG+ zyNqmZW|xe4-7e!Q2z~S=sxjGYaIu{!WBgO?++h=(pGr|=f`4I2ziI^MYopcBi6f#4 z*hr3Z!=>1oAE~;uyx<))8RS8!0ct?GxqmLi}_Tl z%P}K2C>!lvM%6M~iXzBFl8ni|oLfz@g^&GHi({R({~aY;=NAYb;1kHn6v8a85y*xRy468q14zSyAuuX z`GZ~=JBhX14yGC@iE9b)AEUd6&f8e_9xw}b78Lk1)a=-~XRjEZE%Zf1+mDA*?Ykjv z9L%T!BLrm`z7fw*W^dpuVxq--&$PP$B=Ht}m0l7p>`P#@^X>sUeT{D&9U7qV5{LjN zT}|>e0d*KeOnmgYTO<%6`qQ^3(zGS*Ea4QY!?@q=z5s|6-b3G09&2Yf>NAu|^z6Z6 zlKZBRTlazr10FFxGfqY(=#hmw6hw9;*DSrPjF9sDF=0zveDd5@_gT{i0&h4*nvTa! zHjS9R-WUq%J)a!7XzrLBU{20lxkut^q#G-Fn%(C5Yef0T(a!kj^8543yIfhBd}g10 zbMu;4GyZBh^OpI?-;wdnj-B1^iaS`t&pfOOVfsFd1!?CemoaFG{=gUEiXX@V@rHv* zz~DVdx#X&^Q`dBLiEJ{H>YioleYsTjnL+An2!P=Yy+W2|nQ7Og_R}zOtDkH4Xs+ns zEdr`h12V`s1e9rwKv+6wh;e>9%~2az&~jNGKXdN?>+7oHqT1T7AaFrMP!yz5N8h*byXU^ICoV}lC zJ?mL3u_(<5=H6h9KSuBg6~TB!NghI`-iPN;7-0MjgQRnMBxlGUPs1$&4mUWo=j~S zeCJ{1hLWt?mAZ^9yKO~kY>xg6g(nqR#_%e;ueK_En#*1UnhN&b3PAzs_wgaSQNTay zOso=2fX1yV=|edHW`vMe>>c9b0pxV!1F8}lI*oQ37k90uEj}SYK$1dThd~-n329N~k0ExhFyNh}Zn6jse zqOkJ8d`s>mSMjc{FfbqQ`fhhECMR-dQ2Xu0g}=(c*R1fs^0f$CU-3%j(3`SA)U7zx zUNhiZT!r_a7#wFqHV2v)NZvhJ+bh+T4Tshx*BHORd3?VF#IAg3OuAHT67)IzxR z1CBu|(Cun?$RoD^lzRj;r_>?!g;EImP5B_Z@4%#d?&!3}j;^OGw=aEa$G*C{x>;Y! z_8m|P{1FiI;X-Ik8WsvB7f;Wh`V=x`&9n&GcvB6ULtg3RFaRMK18#?#oJPT=sxKo! zSQ2B(nM=e~dTQs84oqkkOR(mqnQ$}RqJ?fpuxBwfPkLG8udhV5vBErw*7CT+3DOmL zy%+dcJV_%$8&1^O%8+bf+x?D~E1!w-0?V2|;quUM@6;s_w?*g_UpAo@oL>$8>KPeT zwAiA#+-`1P?H<+~%h%z;Wz|YGKd4}T=$Cy9Ufa~;Fm1uyLH6owOZ|Se$mg0G5#~?d zq)k7bzB+l>3!T{URiFoKu^cK@WE2q#EiNm4%d$k3m&lLR?$sK{SI`{nXWwzG1({wQ zuOu@@@f?_gNw7AxsmH zCowP;F)=aUX#~*@a+L$tzmynt8OC^sFEQ#@+CpHz3CLUtM@}O61R^4J;#~njXlGub zpQ}S4aW1!G0nz@aKi7-jD~H=dAm+sv7TR!jKlsn}VH4(}{SS^VIB#BZ`pgsK)7tv) z%li2V-SW-JQp67o~L2ZaT_~5|<#o`Oe=P4J6P+!Fu?Bl%p*!b-1 z{`^%~%qGdt&u6X*92aF0gPq2CzWj%h+`;8@$0ryY-OTuZALq^^bUtmG*29g3laupm zOsdqG>-d>$fg1!;pr~WW?~L~MFuFgwmdnS-N2iG3^!q(izdwnz+{k_R_HD{&H^l1P zvtK(^4Y^(0bFOMg3sElL{rhk;G;v_0aAp_z`(KgU*n=J(9{)N+nIZX@H_y{{xq$wj zx+WX;-c366|DPG0eh{QMPtKhn5Q5+idU$$LjMBEBEBxW+2HFRd@0>eJZf>5KaW=0p zZMki7nc8oyhv6)QZTkeJaC`xpXDz_Z={xAMpMKpgu{qnx06cBOf${-=rXT1%4lb#v zW&v<|I9r4BRIcsJNY^Oe>)E&SIL1KPN&qy9ZB!n8SPmj*S3IS;9az`Fdw`3s(aK%j zDlMR?#>{2X8w@%aB=~dII_usOik;ktm<`^0-63?vsUDCwr9j@>ei*^7FMjxJXt$AD zy}}|3C_GB@TQ3l}#4-FFqndA*XzM3*sfyQnA6{^WOVp^tV}=JD3S330zzp4^`9oavu znV1^}QIZshu#;5gxqd!Z>mu$x)zut`Wp^!QPzZiR(3L3If&A7~)j{T7^5`*43L^m> z4K?&pnBn&E7yFQJ$k|BU6d^+&MWgPi64CM8mv2n0;^C+Y0x7Ngg$BV4K}W+1E_x6IVZ<#B2OEntZgsab@pBb z@;pEJWmwm&S#w;G0i7VUmlbHxu9eo)`$!H5 z*;-0KA8@@!OGLztoZq?$BU(ODh+-TjbZT&fQ!Ykc5d+%obP6p_GaEqYp8oN}EF1B9 zSOv&l(E)Kzdd*b;6(|DR7$bFi5Vcq*tZ3<$Ut3MYWs6jA)wG|?rAh)`cY6WZngCM-LtV{}ddC{uq(Ns<$JD(@;H!3xnIwgkR)P)8XP8sw0Ty!YY);3L#MO{AUleS3^5o%if(RqTJ#wCe31@I{iY^A{uU|j*=UF6kPmiTf`G1G~{p}>&+y+f+!oYKv_Fz3U zSPXqs@cF@n4{MDwVu7Cg+#9B@1@Ibe>DBWklrFe8p%(`S2RBLB0|A|%EAgM7aEPd9 zSZd#L{*0u~d`H9@WGFrsU$6dopTCCV+T9xTf4gAj1}*FK0Yzwmb1T0 zjft~=aL}Bt1g~pslmOePQdbj+i55C?&kEq0r>jH=fH_J(3vHJ?GrYmJ;UUtq)S8mhfU9<_Nq{DGnp6^V=2im+ZLKZH^|GK%qCylbi z4QmYOOVFKeo`6m0Q&6xqvx>ealY}lRDmpzoOGimbsR%ea7=KN0F6_3nv15#eEF7ZJ z#(FM{da+Uwym3;lfgkbPayhq5=y?Gbz2C$83fBl0i!}xqnh&~IEIwbiQ}N72mUVS{ zyU1VD;6w!6EqTjCU%9*^*tZwqAtH;;4U1{0`^x_3Oh_`K^H(ZE#_nL((SJ;F3V z3Fgt!5iN*Qm!=ptkv*w0R>*B*9AGW{{qS&qKZ+ZqJ%>%^FP|M@W*VC&1*Is*nRgR9 zwtR16;JOqZHa%p;keb~?j6qr9wxwT9!*e#GPe6BH87XNE#`ijcTw^8*VY3uEh1v>@ zx*xQZh2skEokl4mV-pde`?jT&t??a|tD*w(%ZP;SkHK3`yQ?2SM%geJ0Ft(YDQ~Lp zIl?C)Ku=x<>}z%H?Q)Jgt8{>19G*k}hOXd6XN1GnoD`rI>6>|a1`--=%VLr|DJcr; z)E43FDmv*=Kj_rWFx;&YCbtWX)sbYvsoVzp5MO} z?C-jxo8+8O0b@c}h;)0cct9Kp_Y3d-bD63^u)aLpHRp5KOfdk8k3g%} zJb&mEF96-t;Z&|iPsSHF0xy$tHr?AlJdE7}C6Q*2S)dgHhMJgf(cv^wVWvxDWGq3q zXa?+6qHR&{mjYIpm06EVE@6x#F1UN;-kn@V15qhj+xRSfV`G z@4p`9B-~OEY`o2(mt0oKGW_oI0Xm!xDmk(@g++B-4h$Ff#U);UfCi!f(`I^Yo(;q? zDF7LV^1~B)jWdBRFG0HivsP)Y_3Eg4g7dC=H&;2=vKgX5&T8r_Ca^}$yln64RwiGt zgsd7+)hID$`E=_Ol+@_(%1SDDu&jVUEyR2qlx8-8drLF$4o<5}CO5hz>ZVs#SRM+KGXeZKTrpLq8A!^}0eLxC3+PEQSPDxAE`wL4Lb(Ij zjfXy`^1(&FfY(Cu1Px_azj{dAjN{jp&itCW{5b&(+Q6;9+IO~K|vjAmmO)`&)FHIYr&cm;iyua zxEJP9-N8O!AT`f~dw=_bzcE5;AXRDhZKt6_HB=wM#rXp1pT9Rva$tbEwz0i&rP>?S zT+LifTc1D6!8W1AA?6}jJRz`mWj(wk5@nP_bS$XqCCiZ|#^dm59Wp&@r!AybDYC1@ zYu=$+((i`&m=w$9tkUY6eAwo^Hd@U~rsGmn$Rpmf3(; zL?jTTiE)d_#xL0k%G#VQWitfAxk)Rg7fe8Ml@IKc)ptgi%Iww>%Dk~MtXnc7KrZQr zh2mV64`n6gHoH?{yT*aWsC3JVjWn|-$%Y1N?0K5W72}U9W%G%lAW&|JOPp&eE0{Ov z80AaS&sHr-XpYDLMaLhbWlBhk6;oI?w@X%@QH}gS6+$EttDfUU$K7hwZY&NGmB(T> z#(}3ZUhS$26o^?Vy#)|1K8gK5*= zp;faOEu$05p9^DB$(6ZiL+amCH1+w$9o9tiqxwrN2QpjvPg8Voc=&8kzT{<&PgLwk z%ydj2udUx;V|G&=!rVeY&io2&i_i zwFaJ@nw9{=(Ys%=;cz%eYm@+w(Y!x=c|#t*J6-~Q!mG`BAvJX)BVZO-z|J}JvVmF+ zyLaIUzLF3bDjKLR){bUK&oV&+?pG#aT_I-Cp#_H6oT3l#7ApJqxzvlzqKziobx65P zXmwq;iX76_T}i`E1tL;_jit#;ize@fDPT#Xm&y5}=@8lnQ-2*(4|gv+VE1E%T0BzSLeq(52Oy%CV)9wge>l#&#Che-jC10q|Y;%-4QrS|EGMSq-G-!Y& zV$q4aIqT3pA6bV+cqewEs-%$lz-T?& zA-aWr!FHtOCbLjgqE_WBn=ZGbHdgPOr{j6(j^75@0t{3HoYabAp*e!G>aLr)U-(Vq zaDq{&Rikb>5>D*Wjj$U83429xRj>Lm2zGQrV=5VQRMT#)9dXcnF^=z8I$j|NZML8{3UA7riQbzLHyVzwp4hR;5y&lxeT; zcG)5Q{k|HSBwqPq?-CNyKuXj8ack4L^wm4NU^6g)2S7MC3q68OUjVnqF1;CvdU7g?NHBj}s z{mD^9){W^$w2yo$uva2NIVnJ<<1)>ty;oViHu!M-#NLGpa#0Yl}%u6xf z9NFScKD%FTw?!l&EVu|2E(Q$0U?G+DQ`?+BVW@%M|7x;PHb+Az9dQpeQSl;&y?e1F z1_f&@S_sV402%go&_K=;1Zw%w`V0aJK28pHgzLRysuk9&&(U3)Z5kr6zM04#E9=DY zxBM`0M9I66%uDoh_E?o&lKglhH@@OTVN0u^Dj$Zc8JZ&kGfRqPZyO~eS}=!V4p}>< zLo1Al2HPq%Q@n1p6-4f63x{2F7K+?eX^15;vpQI-xv`wX}O~-l|4jmiAE4R$w+Ah^`oonDCd?Pa- zr!iiH{^6mHiHV0rjG$fZrAsP#+=*S(aS`lz!?CZ7MV`p;nVKx8FRRLle{``3+96}` z%S3Yrt)ZO2lp3m zFZWYs{EF+uJqAEbgY~~W+pqUw!>N$ztH{qv4_wuE{R~ugwD;N`P&j!@OkVMP25!| zpwUi@(^oxQ_wqL*ulW!y)Ekj)COHu1_6U+5~! zp{lDxw%#kksI9#IR6m7o<06)M7Z$Qiy{S?!indhJQK#kkMfyP_s>lSj8HdNK>a|i% z-FNx%bWG^SiTjI&z56ZtWa;(dsf2lk?03uGNKk)5=SB@+|5xVZrjNmyEK@LOP4gC@ zd<~@QJ&Pf&F6#8A16kpq==t8F_+#yDykH7}myfN7jq8%!?~bu>Jw#rnA2HpD>4VOA z`Pe*n)B*ZnkUnL|w;y!b?KiBK;1-!5Nlf$`{lk6KpAY2dMuPU~=uB=W%{=GCqi+U; z3q9#+G4U|b-2Ll2u^Tg&Lo4^@bzOk~frK1^oM*HK$PXy`<8~ObusCRhdY{Mu)!Fvz zn%V;(r?E<^eT4UjzGTxSs{!hX?j#=J3_+*xws8Ac;cwDq-nDiT-bOPX!mg?Eh%`xl zepF9x#kvT#HVoI5FX7hd+AO&N0t9sfVKp+2%jyLpLRuA%p=%>196fE#*t)RQVWa-{ zh=&NxFLVJ}ld;(ouxJx1H6>nn$s3tCvtn%3x})taEe`jh>d|AgpPYICXb2& zX$2t+Ua2<8Rz9*w>y6`hVCxvqNo+*WLAf^gBI^J))8lQyyAfaX%##QY2&;W#P<(!L zf$#f@;jm-+!ilgd3`_TXJ%K?$jQC(cc+&>n4JZZD0Pi%qkO3s2*<0G;A*xp> zgbcU^yBO0+4G_jLq;5{h2r~M~6f!1G)A2TpseVf}%YF#LEgB1SaSPDRcO-^lZV}fF zy3~0BdI1;NT_aeOMgB)JzPjS7SRJ@&a{$GC6`+nu&u^o|Ba)_;m!u9c2OO_wrdi=^ zR2xL>LIW-)-HaJdwrd~Cw&oht*or+#?Gf?PHUVFdJ4=4@1$~PpMY`E6m5w_c6$MLI zvdSTu^7UD2=}F{5)`J-LYy$4bPk+9jWP%lE>ZrPvuu$0VPx1}M*I+x5VyneNaZ@`d zE2S8QYnP=s@}o3;2&&N^$9w0HEuM?S45?vvrSF2;+R`)AgGF|yZa1a)>Dd&6T>+q< zyszP4mdIDM-z9%ASx*n$^73?U@X2mgX&ELd@-K4HC`p9B7ZiD6DZyj8@GPsA8!m@m zFh1HMuM#|j{Gqw57R|(;W!--QFCNZ$zgPhsGiHpY3HqX%{7HR;1?KpQ0!0HoX3!tC)%nF&3_bTh0OYF-p& z%_#|Fo%xLc2#a$Y>}4{iE|^MB;c)<^RX^UJ!7tcKhx!(okM#ct8P0JPF0mdg}sYv-KP=``oug^bxCx_Q&6zYT$RXSmqd)FWm>0V&ks zL|-3T4psMz);An$ewb9@WYQ%!AQ+o(>RA|jlC`*5GjVJRi(2CBu}}d))zUlqQleJ^ z!cIRp5X?J+X3Y0s=EmKOu>nx$Te$eE_i8KW44GxFnr1=pbe&#w;`{;w<=`AL+#LZ+^1Hq$qHB7gy;Z*D}v~r(7j|T&7@?vN+8%8wFDm zw+Y2=k3$4p^)+mz)8$$9dt}{+{8T zrvttSe^CkmP)l4&;decLW7HAHM=?Zieilb=7S{vKZ#{sg!O){SCMjA_#Mycmyif^1 zUz~F}S#0#Wg5BjlGY)MY79p{M*=?cn?gwxDb{|4IC{q{2abpaL5I1K`*0#D6WBn^G z{)PW`LKE(5>zEw6u-ymz3Tck4+5-{BxnRW&;1(LY+7Fd;ZVSG3H<|G$$zDhu=-Tw= z=n5yOie)EQJQ`^FYx+Q%2e&D%qyC2g>5k%y*wE(To}E_Ci(nIyzuLzLCzUPuYmO2Y zZ^laU)zcAQX+lRrcz{nE;%hm%Vzk^dv|P7aDU(WUD4wxOKiS`ZNn{CnHKq?XWM1(_ zUaCmy+imU7Gv$UW))^Hg;&=H`{aiM~>x%_fJL~rj#-1T&oN#uO+%71|Db>Wb5&X&V zqc*+-(*HwNUsCGU1VLoS2Y!mDR2wY2Mcva*@%^qKhSMQK+}z&x^j-bI@33iLys1GL z`V8*=upPa(Xjr4r=<1>sUS>Vic;YH|4`%cBJNcPj3VD5|n6f#FNisybw-BtBx&5}E zW!_po>BKVUmJ+;DNP5U+9AS`ObXWB$x=^$Q`nz)d46FfURt#ZQ^WZ2a0WsH2;+5xD zr~wh`1qw~_nwVv6E?UYX%<_~!}bI5`m z+0fQlPTDz@El78Aq0diocUiP&V<=|RzeqlgND#K%Drj=n@}UG!5RlU)rD?q>n8ig~mJ;;9tUUfc4VyOxXNSHQgPqR0Dhc zneo5C&Hwy6sfN}wM!qa{K5%*y4G`bRVF!u&&F1{@X}-*7o2LaoF{rw1>Pg<_P~XTzi> zc$%tJ? z8@vQS$4`Miqm8}1jb{eI{X6yK{5%6(KK>dN6%~Ml<*#VNf`UXv;E_uct)$kiVT_>< zS4-CbvTD|9rS97DaCNkLKA7A1do#9CjT~f*b3ujOa*6?gvl{aqQxQOBteikOQmQK1 zn&9UE|B?S(aQ{xLS0HV*#O8dyahp0e5R)UaB#b|cG5>STk{0MG6?7$b{>SId6fG`V z>}t=0-w*Ylzl+v*X%L9Q&JGe8pY;Hnvn`PH{pLsrh zo(Pn}bKiM+(LX3SpOGTcLZtWl-MRRn` xjhx&pX5a@8$K*B982UdKy`Mv%7*=y~p&I`v^hAjx`~vus5|e+F{Xoy-{{ZkNhdBTM diff --git a/docs/static/img/boltpy/signing-secret.png b/docs/static/img/boltpy/signing-secret.png deleted file mode 100644 index d32afa03e670f36d023355d91c76f0f475a690f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 289939 zcmeFYWmH_-wl&I*LV!RB!9BRUOM<%=ZYiLU;1sSQL4t+g4j~CnaCd@h1$TFMw>Puz z+57Bs+xOje|GZ!Ct=6bkEasYP%9vyH-p3$FSy2l83BeO2BqVehX>nB~q$h8YkRIhD z-vf72!dk|_jS>qnF=cTJxSbW$4GBp)C??iS88U(6KUQDKG4tI9^YgEFCa>R{L=fpH ze@3HDv~ei1Cg@FQeioYj@eSURWIk5$OM&sY7#&7>%eRhV3*bNkUl=5fp>amKEw3rTUmKbn4> z*}g{F6L^i(#H90)d96{5AgSZVu9NfL6 z5%9?lphL2$5v0U=$Nh?^KanJNd3qUTK`fd4VF0g?2&L&qD!1DpF~%2<{pj~hm5>P+ zheOFC2BpY@NsKOzr89hF4c-i6hOEq4u9&r*({E*dj826Qep?EtB75ol6LSqF;qQ$& z(A+${mWCm64#`$R`P_Vx7{tE!YJQgrq&36#*<+$&F_`$kNkyidv(`=vH+dqG(a0zu znvfProIiZ@4|(ix_1BwGj~9h5QQp775`3?;jwR`b z4;ACZvT4<#_>BCLBIp4{kn~H3AWtKb;%HiO*`Oyz0xELXqC`}R&u~9JUF1Ia>?&DD zE*yp3MfLFe9~O$AC}yM$*`q_Lvp!oaqd7d)mM);^>7sXF(N;>OV2R-AWIlX2A?2%@ zUPfo!9qaJzI>(MaLN;T0_+D;2{kPog8_4q!S^aM{*>S@(1RvV7P%J7qm2{Aq0;tpb zzA%0H@kRZY^T4KsnfaJGhDCjuts{wknvK}mmyq>?W6n!?_hG+ldSde3-WAg&5TAiM!T?769d>O zt4Ik%OOjDa1Ct>lrbp)|z44A|${T%{5cH&UWWv-6!($xShLr=?RY6h%3Z-V;RiSw=$V%R1PB0`R7ge1>eM`MCw?Wl7FCph5 zS3y1Ln?|Oy^iIj7oKrz<=8 zHXm)8ZWN4I|Ee#P8Y&%4%H~nC&70M(k863@A`~bWfQuo*d;Li`$My$lj) zF{~iB*0W-_su*_+{T}W}njlc}c3DBU;76gg?+(dSp!1|N)LCwuVA~*!fQ{aMcv9uZ zw}uIUDYL1)vc|118?61%AhDuG7m=StXRn*ls&h;DAb|H4U3(m7CySr-*zi#5MXfSXw{5xKUHwsz{ zl?u9e2ooE#QNw!s*=7u=4U7tR4D^ltjicIcIV*am#&x&#S8h5xhNU*uc8*%dx_O3N z3ilX9JVdJJo;70JlH4BMB;C(`H~((&-RY~orue4drpK?TUt7HPc<=rp<-^(|p1@yX zqGF=~)7bbQDZlEd?D3A-A9bXltOQ8b6t;O`rLtr)9rbS*=GaZ19{)}+O^u3JIY#f+Y~?j z2)n_=#ndDidm%#a?R}l`px!UkuU*{K$jZpcXzqKH3?cQ2ymP7^XEi*yEm~WRS$-rb zQRw8Hv)p3QA~14Y#$Kj2s^McxoBCpdiOS*XvO!Rz$+ua49*pqT+O!@ zn?sz1Wlk1|!TO<|K||AXGl)rDKU&vx>;*Z4U>>z4nE-Pwb6`wVG@MhDgNNa)*Rf@z zG}EQi%0!~10H1_|yLUY%usiSQV;!Aa*b`sTO!J<~-aGO;X!H2{?)Tvw#%3gm8EgzL z8dFwV6Fp`k3C}s#)~d^@BdeK?Lf4p5&+HoZu6IyBKI}r(!AWFh)Eh6QbsZ>ZOK#H+ z@zFJE{O;IQn_(}P^aTA0?hEHT%55_x57V6nBU6Kx zk)f)_A)Z6 z6svWMf5`xBirayE)pna6gTZ#1(`YDB!H|BC!3J`vK59KWSLb;Y$TY~LS$SzS)7bY$ zx2pmn0=ss!^w>YWq(90nAr2*eC_u6oIx}%uI)x}7aUY=?F|>5BtZ|mu zIolLkPQ4aFoGQ+Gzzmxj+HWeZD#t9wVu+f2ukRy#WG*v~-mEvrirEC7l5z{wT_E2{ z+Y70P)HYO-*Qe*Pr57))SAAnrNL7&XF25>YjXx;vUef8%Oq)-p6=bt>=pkF^^R8RT5wH zT~xl@Vct23(`Vr^ux}h3X3Jb^Ro~0YDtM?T;G*w%GZ==#!_RNbZ#$FhS-KT_JUTQr zQE50wS2g70G9YwWzj(EI)tW(_t+rrz}dW4|J5x<8>y4X?&b1n&Ix>ajpl}&B8o`)dt?fW-BqTu*S39V&CCrh`2xewsBSf)R+ekrXVIoAK$)&)mU?&DM zw~%&sfT_7FLX6!ljrmL{M1;u%UHL%=)-XpXnX9#x4V>Rqh~m$F`N8+!w^=C2{%qoC zDMa!5_Y29i6qL!tY#m@^T+G}|#%!FdWITM#Y}~AzTx^VF?5u1&EUa8CtlUhjZ2YXe z{OoLG|GFr^+Z;?x`BlXw|Mf2Ln-GP$qoW-^3yX`33$qIcv#o;}3mYFF9}6ox3p+a# zXu$+`vvGvFGTFdi{?&sx3~uaTVdrRJYeV+CN2rmllcNv?80mja!P@TcZf)TIS|+ey zEUr*H7B*(q-&6Xlp@PEy+|=6o@78cf31{$@e~tJ5*fAX9W(Q+Yg~4r|9E@QS&M+It zmw&y?#Q5)L?VKE}{+y|aF$>HJW(}Ib!NA!5ewCfMt)ng6-1h%6kH7!?_iXI=#T;Nz zM_UJot*zBx8>ReLFJxk3zc+@Lj8*|^Y+>`eYr5aV`qxu1ai}9qhyt|WV`AlCV&{dh zvGcR?@PqHaKUx2)sRA$t6R0Eff7jU7#KP3=f74V!fnUZ3?g+IphRKKvQGh`)TUeOz z^O&$hpoJ^)XrhL3SJjTXG#vFh3rsQA&jt|u8 zpS}Jbl?mt(%FYYp;pJmx;^Z>nX5!=o1LEW3U+_h`gyt!y0>Y)!zLgSYH>KnmW2b9bm7`z()M<43OpjHUl`+`F|e&|IRr6-yZ+J7TDMvYGVciZkC1O_l#J6 zJEZ^G9+v;UbASHrAI|Hq=D;KV{`$8o2EY96%waa5YX{(}!*uL-_QE9F#OA7i?EPYwt{-36N@iMB5eUPK1q$HX?W0g@CNfcSa?_N5Do{OCw zORn{DFBK-YzNXk#5=WuR5?t#T*O!lU;Ct6X=_>CfGvSe%sxCdm2~-s zqI9#kq$IO$1Ea98aOETHblDhsE~ib1oLo;KAL~En{%%jnNYqG2N5?+=UITu6dpk}n z&gD+2U;A1Dy{XTepZ-PI>WVGTl_B>9meOHA!X(Pv_j`)Aua-Y*V?34m-oY{9{2iI? z8R4x;sa%Wd%EMuohIN4!R^#?hv$ZayEx8VsVruXElDJQHX96=Dk4jZo8G9Z*c;L7` zF3icud8Ps3!$1sWA$a-uz3U@hFR-G4#fyx@LPrmimzUqac>M37(xMm{8HWl z?k!RE+u}#$i$?;9JQ5K8Ht-#x~v0vo2Y8(s)$TZsgUlD z8P|*u(JadSMoPNO_aYvuo%WGu0Usrv6K(%~jmw>ZogJ&8$g6aS)oh&%^Vn1&oC947JZcV{)$Gb4VJN95*Mj16nydVVhu^; z%kVHHZ1K5vg0^f%Nmctl{V?s+Q~&QB;mV+!6||(BVJEu92NOO$-=xeaH?$&Cs3_@Q zR=;Vv|ISvYkASPMua7Q~!cUn~LhU`ZLJ|Q10nS;~KgQ;*7r*w%|2t%3=Otuu5&nk+ z3avh24}OEQKm;3q8g^_aBNG;{Uu|X?-w5&eOB5IHEWyDM^cwdH3}n z|9JFOkTMuS;@3LsFwbv85Upe)q~LeTw2mxGy9Vv`RgZt?3;w>S@Cya=E2ci&sg#`E=|k_kNiTh{bgf^bkP#`z_$LRrmz7u6gKj0(x zqi7Wuztd1o_yAh=`b&4AmTDa?$ix6cGgY_(5$(iJ7Gskx;yy8JyK0_B6^Fsu|$={@tXj zB>D-Jgo;tI7DYsSVj=+{A@0@0(xV(XIh)Uh(Cq1q=$@773Q_HeRnRM!oWn#cYZt&jj2-C<=y|}zg z6>?b}$WXf1*sw~YI0Z)O$fj7&_1i(ZQv8!u51pt%eJ&*|yuRbWtkIJxKa_U&6!(@fPI zNDkzl?<-0yd_27Pq@=NY?L5o4f)@-L%UG6SN3rqo>q}eTJ~JRonc>G}Ii?n03X!9!@_ihJ-_y{DQ2P6%#>)cV%>Qyigzr#i zd`raA=?83w`ujt7ell{uB1S@D;+y@27Iea~xyHM9MQBGW8&G@=ui2}X#~7!(6Ji67 z7l$iXXM1y10|*-%8(?DYM3FdXE#gvAhSi6Am*|AEjUIByP>1Sxm%Z7EB75T>iCi6F zq;uKOmvA^-R9arXcfR?97Sr=zbTwTF5f2ZKH3rPE@bJdF`P*In+5-K?%+Ea|fvx`E zu2DR%mk)}KrOa;F@)VLPysl1v=w=lpBqZ!Url*XBd3k!epX?^2#>Sc%M`#H;br0k8 z>0%GO!G6*P2B?(E?>66byI;f_d6hxwH(lcfAIVjZVekmU7iyoX_5JYSzQ|D$Y(gnb z7+4rP6<@;Fjg=nFs_N<$=L=y^R}qtS%nZ@j1Mek5a4cHa?CtF_NQJN0(i&~ArZwnd zblTx}v-K$gE?U~UXS*|5nut_j-Eq>C7ee=KXY26UO^Rf5DE+Kvufs!1QK`TD^W-7X zZJz73m2`+k_WM!3uxfX6)?+D72p+Nh)}c}K0}9QKtJ!SMIp82ALQ@DC%l^@a6$_Km zGwq(1l3E?x)$Yke`w<==mSpa6vKjLb6{pVaN|HLRxH_5iWQTrth9h*2uo0ibbbNdq zlB;EL?S~RZz&)sHpvw|1yV4a!qfwx9a(g4-uNpCBKGNPL=y`3|`MJ5=a>SsEtrd_S)z1VtyPRQeZzHbm46QheDpkWmfx>*idieol7&fVs6 zKaMw?yrsYj3g+jBX`kL)oh`M8D=5f~cyA(;Q$&~#cd8h7ggr+-b}M`o2#x>v@niMT z>THb@2Q{^1TlV?E?>Kw!grk`qO-$=W3+B%{*9)W8&S$&3yB{8-lL)#Z`wUhol>|c{ zP=@OoYsGrn$P8R7;rEn0@n7tn;Kb6&q&j?XvE&yT)ht8r^%%lPPUkSP8-8G;-l`G# zv%w!N!*eC1bfOeq-cpRBZ6TMIHhX(y2e*fsd6Y6HMMsx8&rFtEjxFO$tWDoS$51si*S`A8^GC(TI4>kJkqjKPgJsZ}Kws zw70f~;tQ+}b%eV;Rev?aq+3QL;{6^%G2)S(78B!iahO4;mL)DB>CnX{p=MNE1<6s( z*p^d2Jj_*Ys4Fck4GKbOVLdoNNr$F`_dR)n_5=l8MpjmX_Ql?8V-mO5rGE%+ETgtg zx~ISBr_a#%{r&wI2F>l6N=@pxXQJk!Al~rt@=i`oxv3vIIKfNH%CJ%Vb1W>LEiCMb zcp^V}5(=)cF!kJM`WSX<>ebImo-* z5&e9O_YxIqaD+X_#fFBbcuOMmiA!AxAE9NsTG|CY9k!8BKfDfQA1vszCW*1c@p z5l)94Eb83Gp8sBDd!qE}AgW`maORf>xl1f8Dk>@voz!_}vfLB4w)rwNB!rmnZL8rC zu6a)94}k!vL4!M=!*XX_p#k=M|NRqRn5n7Ftw&TiV#jG~bSzgp?<3an^71mu^NzV% zA0EVjD(8t>{iWqt0hsgf9$r<~C@?~uMsF_%S#}r3L}m3{jTBa>(Fnb|TpV-CQea^1 z$V*z@^~-Y0ByQW_upp?`NLUh&`{{PI>%k&8I;!#H^*(prnoJ3@@&aHHAOLN-uI6gkUc&T03?a-t@xf%oz+Lr%d zeGfJK4&0LE@nh7-{j^YD^vs6K*;<(~(#pSSkGx_d<%lT}BxGm2Vx|9*CMA~h3NrsK zBP&ZORdAy-qD7}Wel0E-Dkvz3T6E7bRa8tY@K$(vcBa;!$Qqb8sZT$>{A6E5&x&TD z0Wq6{tEZ^vg2zk0BD5yHvNw*%p4a4-a;U!EYvf52@iBr;>OΜ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

    =$cTRzP6g9AUM*{+>;Kt{TOp-U+J|Vgil81LN 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 z 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

    Bolt Bolt logo for Python

    +

    Bolt Bolt logo for Python

    From c0ea30f8699ad3fe87f0b199c94dadb46420bba7 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 22 Aug 2025 16:11:49 -0400 Subject: [PATCH 135/282] feat: surface ack_timeout on the handler (#1351) --- slack_bolt/app/app.py | 14 +++++---- slack_bolt/app/async_app.py | 13 +++++---- slack_bolt/listener/async_listener.py | 8 +++--- slack_bolt/listener/asyncio_runner.py | 2 +- slack_bolt/listener/custom_listener.py | 6 ++-- slack_bolt/listener/listener.py | 2 +- slack_bolt/listener/thread_runner.py | 2 +- slack_bolt/logger/messages.py | 6 ++++ tests/scenario_tests/test_function.py | 31 ++++++++++++++++++-- tests/scenario_tests_async/test_function.py | 32 +++++++++++++++++++-- 10 files changed, 92 insertions(+), 24 deletions(-) diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index 86909ed18..60f20ea9e 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -59,6 +59,7 @@ info_default_oauth_settings_loaded, error_installation_store_required_for_builtin_listeners, warning_unhandled_by_global_middleware, + warning_ack_timeout_has_no_effect, ) from slack_bolt.middleware import ( Middleware, @@ -912,6 +913,7 @@ def function( matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -940,15 +942,17 @@ def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) + matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener( - functions, primary_matcher, matchers, middleware, auto_acknowledge, acknowledgement_timeout=5 - ) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ @@ -1424,7 +1428,7 @@ def _register_listener( matchers: Optional[Sequence[Callable[..., bool]]], middleware: Optional[Sequence[Union[Callable, Middleware]]], auto_acknowledgement: bool = False, - acknowledgement_timeout: int = 3, + ack_timeout: int = 3, ) -> Optional[Callable[..., Optional[BoltResponse]]]: value_to_return = None if not isinstance(functions, list): @@ -1455,7 +1459,7 @@ def _register_listener( matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, - acknowledgement_timeout=acknowledgement_timeout, + ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 294fb8b0c..906359fcc 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -68,6 +68,7 @@ info_default_oauth_settings_loaded, error_installation_store_required_for_builtin_listeners, warning_unhandled_by_global_middleware, + warning_ack_timeout_has_no_effect, ) from slack_bolt.lazy_listener.asyncio_runner import AsyncioLazyListenerRunner from slack_bolt.listener.async_listener import AsyncListener, AsyncCustomListener @@ -940,6 +941,7 @@ def function( matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -967,6 +969,9 @@ async def reverse_string(ack: AsyncAck, inputs: dict, complete: AsyncComplete, f middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] @@ -976,9 +981,7 @@ def __call__(*args, **kwargs): primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener( - functions, primary_matcher, matchers, middleware, auto_acknowledge, acknowledgement_timeout=5 - ) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ @@ -1458,7 +1461,7 @@ def _register_listener( matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]], middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]], auto_acknowledgement: bool = False, - acknowledgement_timeout: int = 3, + ack_timeout: int = 3, ) -> Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]: value_to_return = None if not isinstance(functions, list): @@ -1494,7 +1497,7 @@ def _register_listener( matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, - acknowledgement_timeout=acknowledgement_timeout, + ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) diff --git a/slack_bolt/listener/async_listener.py b/slack_bolt/listener/async_listener.py index ca069b097..0810b91a7 100644 --- a/slack_bolt/listener/async_listener.py +++ b/slack_bolt/listener/async_listener.py @@ -15,7 +15,7 @@ class AsyncListener(metaclass=ABCMeta): ack_function: Callable[..., Awaitable[BoltResponse]] lazy_functions: Sequence[Callable[..., Awaitable[None]]] auto_acknowledgement: bool - acknowledgement_timeout: int + ack_timeout: int async def async_matches( self, @@ -88,7 +88,7 @@ class AsyncCustomListener(AsyncListener): matchers: Sequence[AsyncListenerMatcher] middleware: Sequence[AsyncMiddleware] auto_acknowledgement: bool - acknowledgement_timeout: int + ack_timeout: int arg_names: MutableSequence[str] logger: Logger @@ -101,7 +101,7 @@ def __init__( matchers: Sequence[AsyncListenerMatcher], middleware: Sequence[AsyncMiddleware], auto_acknowledgement: bool = False, - acknowledgement_timeout: int = 3, + ack_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -110,7 +110,7 @@ def __init__( self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement - self.acknowledgement_timeout = acknowledgement_timeout + self.ack_timeout = ack_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) diff --git a/slack_bolt/listener/asyncio_runner.py b/slack_bolt/listener/asyncio_runner.py index 98d3bf4f8..81f9e6106 100644 --- a/slack_bolt/listener/asyncio_runner.py +++ b/slack_bolt/listener/asyncio_runner.py @@ -149,7 +149,7 @@ async def run_ack_function_asynchronously( self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= listener.acknowledgement_timeout: + while ack.response is None and time.time() - starting_time <= listener.ack_timeout: await asyncio.sleep(0.01) if response is None and ack.response is None: diff --git a/slack_bolt/listener/custom_listener.py b/slack_bolt/listener/custom_listener.py index e2977effa..2b73018db 100644 --- a/slack_bolt/listener/custom_listener.py +++ b/slack_bolt/listener/custom_listener.py @@ -18,7 +18,7 @@ class CustomListener(Listener): matchers: Sequence[ListenerMatcher] middleware: Sequence[Middleware] auto_acknowledgement: bool - acknowledgement_timeout: int = 3 + ack_timeout: int = 3 arg_names: MutableSequence[str] logger: Logger @@ -31,7 +31,7 @@ def __init__( matchers: Sequence[ListenerMatcher], middleware: Sequence[Middleware], auto_acknowledgement: bool = False, - acknowledgement_timeout: int = 3, + ack_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -40,7 +40,7 @@ def __init__( self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement - self.acknowledgement_timeout = acknowledgement_timeout + self.ack_timeout = ack_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) diff --git a/slack_bolt/listener/listener.py b/slack_bolt/listener/listener.py index 51dadae56..7685f3c7b 100644 --- a/slack_bolt/listener/listener.py +++ b/slack_bolt/listener/listener.py @@ -13,7 +13,7 @@ class Listener(metaclass=ABCMeta): ack_function: Callable[..., BoltResponse] lazy_functions: Sequence[Callable[..., None]] auto_acknowledgement: bool - acknowledgement_timeout: int = 3 + ack_timeout: int = 3 def matches( self, diff --git a/slack_bolt/listener/thread_runner.py b/slack_bolt/listener/thread_runner.py index 61e8d6129..378ca1bfa 100644 --- a/slack_bolt/listener/thread_runner.py +++ b/slack_bolt/listener/thread_runner.py @@ -160,7 +160,7 @@ def run_ack_function_asynchronously(): self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= listener.acknowledgement_timeout: + while ack.response is None and time.time() - starting_time <= listener.ack_timeout: time.sleep(0.01) if response is None and ack.response is None: diff --git a/slack_bolt/logger/messages.py b/slack_bolt/logger/messages.py index 3ec1fef8a..cffdc445f 100644 --- a/slack_bolt/logger/messages.py +++ b/slack_bolt/logger/messages.py @@ -1,3 +1,4 @@ +from re import Pattern import time from typing import Union, Dict, Any, Optional @@ -331,6 +332,11 @@ def warning_skip_uncommon_arg_name(arg_name: str) -> str: ) +def warning_ack_timeout_has_no_effect(identifier: Union[str, Pattern], ack_timeout: int) -> str: + handler_example = f'@app.function("{identifier}")' if isinstance(identifier, str) else f"@app.function({identifier})" + return f"On {handler_example}, as `auto_acknowledge` is `True`, " f"`ack_timeout={ack_timeout}` you gave will be unused" + + # ------------------------------- # Info # ------------------------------- diff --git a/tests/scenario_tests/test_function.py b/tests/scenario_tests/test_function.py index 41290de8f..0a2152892 100644 --- a/tests/scenario_tests/test_function.py +++ b/tests/scenario_tests/test_function.py @@ -1,4 +1,5 @@ import json +import re import time import pytest from unittest.mock import Mock @@ -149,11 +150,12 @@ def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkeypatch) assert f"WARNING {just_no_ack.__name__} didn't call ack()" in caplog.text def test_function_handler_timeout(self, monkeypatch): + timeout = 5 app = App( client=self.web_client, signing_secret=self.signing_secret, ) - app.function("reverse", auto_acknowledge=False)(just_no_ack) + app.function("reverse", auto_acknowledge=False, ack_timeout=timeout)(just_no_ack) request = self.build_request_from_body(function_body) sleep_mock = Mock() @@ -168,9 +170,34 @@ def test_function_handler_timeout(self, monkeypatch): assert response.status == 404 assert_auth_test_count(self, 1) assert ( - sleep_mock.call_count == 5 + sleep_mock.call_count == timeout ), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times" + def test_warning_when_timeout_improperly_set(self, caplog): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse")(just_no_ack) + assert "WARNING" not in caplog.text + + timeout_argument_name = "ack_timeout" + kwargs = {timeout_argument_name: 5} + + callback_id = "reverse1" + app.function(callback_id, **kwargs)(just_no_ack) + assert ( + f'WARNING On @app.function("{callback_id}"), as `auto_acknowledge` is `True`, `{timeout_argument_name}={kwargs[timeout_argument_name]}` you gave will be unused' + in caplog.text + ) + + callback_id = re.compile(r"hello \w+") + app.function(callback_id, **kwargs)(just_no_ack) + assert ( + f"WARNING On @app.function({callback_id}), as `auto_acknowledge` is `True`, `{timeout_argument_name}={kwargs[timeout_argument_name]}` you gave will be unused" + in caplog.text + ) + function_body = { "token": "verification_token", diff --git a/tests/scenario_tests_async/test_function.py b/tests/scenario_tests_async/test_function.py index fc1299e55..3f8b7a722 100644 --- a/tests/scenario_tests_async/test_function.py +++ b/tests/scenario_tests_async/test_function.py @@ -1,5 +1,6 @@ import asyncio import json +import re import time import pytest @@ -160,11 +161,12 @@ async def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkey @pytest.mark.asyncio async def test_function_handler_timeout(self, monkeypatch): + timeout = 5 app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) - app.function("reverse", auto_acknowledge=False)(just_no_ack) + app.function("reverse", auto_acknowledge=False, ack_timeout=timeout)(just_no_ack) request = self.build_request_from_body(function_body) sleep_mock = MagicMock(side_effect=fake_sleep) @@ -179,9 +181,35 @@ async def test_function_handler_timeout(self, monkeypatch): assert response.status == 404 await assert_auth_test_count_async(self, 1) assert ( - sleep_mock.call_count == 5 + sleep_mock.call_count == timeout ), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times" + @pytest.mark.asyncio + async def test_warning_when_timeout_improperly_set(self, caplog): + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse")(just_no_ack) + assert "WARNING" not in caplog.text + + timeout_argument_name = "ack_timeout" + kwargs = {timeout_argument_name: 5} + + callback_id = "reverse1" + app.function(callback_id, **kwargs)(just_no_ack) + assert ( + f'WARNING On @app.function("{callback_id}"), as `auto_acknowledge` is `True`, `{timeout_argument_name}={kwargs[timeout_argument_name]}` you gave will be unused' + in caplog.text + ) + + callback_id = re.compile(r"hello \w+") + app.function(callback_id, **kwargs)(just_no_ack) + assert ( + f"WARNING On @app.function({callback_id}), as `auto_acknowledge` is `True`, `{timeout_argument_name}={kwargs[timeout_argument_name]}` you gave will be unused" + in caplog.text + ) + function_body = { "token": "verification_token", From 67b873ded975b3ec35f07f954f0cbc1a3dceba43 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 27 Aug 2025 14:49:47 -0400 Subject: [PATCH 136/282] version 1.24.0 (#1354) --- .github/maintainers_guide.md | 2 + docs/reference/adapter/aiohttp/index.html | 4 +- .../reference/adapter/asgi/aiohttp/index.html | 4 +- .../reference/adapter/asgi/async_handler.html | 4 +- docs/reference/adapter/asgi/base_handler.html | 4 +- .../reference/adapter/asgi/builtin/index.html | 4 +- docs/reference/adapter/asgi/http_request.html | 4 +- .../reference/adapter/asgi/http_response.html | 4 +- docs/reference/adapter/asgi/index.html | 4 +- docs/reference/adapter/asgi/utils.html | 4 +- .../adapter/aws_lambda/chalice_handler.html | 4 +- .../chalice_lazy_listener_runner.html | 4 +- .../reference/adapter/aws_lambda/handler.html | 4 +- docs/reference/adapter/aws_lambda/index.html | 4 +- .../adapter/aws_lambda/internals.html | 4 +- .../aws_lambda/lambda_s3_oauth_flow.html | 4 +- .../aws_lambda/lazy_listener_runner.html | 4 +- .../aws_lambda/local_lambda_client.html | 4 +- docs/reference/adapter/bottle/handler.html | 4 +- docs/reference/adapter/bottle/index.html | 4 +- docs/reference/adapter/cherrypy/handler.html | 4 +- docs/reference/adapter/cherrypy/index.html | 4 +- docs/reference/adapter/django/handler.html | 4 +- docs/reference/adapter/django/index.html | 4 +- .../adapter/falcon/async_resource.html | 8 +- docs/reference/adapter/falcon/index.html | 10 +- docs/reference/adapter/falcon/resource.html | 10 +- .../adapter/fastapi/async_handler.html | 4 +- docs/reference/adapter/fastapi/index.html | 4 +- docs/reference/adapter/flask/handler.html | 4 +- docs/reference/adapter/flask/index.html | 4 +- .../google_cloud_functions/handler.html | 4 +- .../adapter/google_cloud_functions/index.html | 4 +- docs/reference/adapter/index.html | 4 +- docs/reference/adapter/pyramid/handler.html | 4 +- docs/reference/adapter/pyramid/index.html | 4 +- .../adapter/sanic/async_handler.html | 4 +- docs/reference/adapter/sanic/index.html | 4 +- .../adapter/socket_mode/aiohttp/index.html | 4 +- .../socket_mode/async_base_handler.html | 4 +- .../adapter/socket_mode/async_handler.html | 4 +- .../adapter/socket_mode/async_internals.html | 4 +- .../adapter/socket_mode/base_handler.html | 4 +- .../adapter/socket_mode/builtin/index.html | 4 +- docs/reference/adapter/socket_mode/index.html | 4 +- .../adapter/socket_mode/internals.html | 4 +- .../socket_mode/websocket_client/index.html | 4 +- .../adapter/socket_mode/websockets/index.html | 4 +- .../adapter/starlette/async_handler.html | 4 +- docs/reference/adapter/starlette/handler.html | 4 +- docs/reference/adapter/starlette/index.html | 4 +- .../adapter/tornado/async_handler.html | 4 +- docs/reference/adapter/tornado/handler.html | 4 +- docs/reference/adapter/tornado/index.html | 4 +- docs/reference/adapter/wsgi/handler.html | 4 +- docs/reference/adapter/wsgi/http_request.html | 4 +- .../reference/adapter/wsgi/http_response.html | 4 +- docs/reference/adapter/wsgi/index.html | 4 +- docs/reference/adapter/wsgi/internals.html | 4 +- docs/reference/app/app.html | 139 +++++++++-------- docs/reference/app/async_app.html | 137 +++++++++-------- docs/reference/app/async_server.html | 4 +- docs/reference/app/index.html | 139 +++++++++-------- docs/reference/async_app.html | 143 +++++++++-------- .../authorization/async_authorize.html | 4 +- .../authorization/async_authorize_args.html | 4 +- docs/reference/authorization/authorize.html | 4 +- .../authorization/authorize_args.html | 4 +- .../authorization/authorize_result.html | 4 +- docs/reference/authorization/index.html | 4 +- docs/reference/context/ack/ack.html | 4 +- docs/reference/context/ack/async_ack.html | 4 +- docs/reference/context/ack/index.html | 4 +- docs/reference/context/ack/internals.html | 4 +- .../assistant/assistant_utilities.html | 4 +- .../assistant/async_assistant_utilities.html | 4 +- docs/reference/context/assistant/index.html | 4 +- .../context/assistant/internals.html | 4 +- .../assistant/thread_context/index.html | 4 +- .../thread_context_store/async_store.html | 4 +- .../default_async_store.html | 4 +- .../thread_context_store/default_store.html | 4 +- .../thread_context_store/file/index.html | 4 +- .../assistant/thread_context_store/index.html | 4 +- .../assistant/thread_context_store/store.html | 4 +- docs/reference/context/async_context.html | 4 +- docs/reference/context/base_context.html | 4 +- .../context/complete/async_complete.html | 4 +- docs/reference/context/complete/complete.html | 4 +- docs/reference/context/complete/index.html | 4 +- docs/reference/context/context.html | 4 +- docs/reference/context/fail/async_fail.html | 4 +- docs/reference/context/fail/fail.html | 4 +- docs/reference/context/fail/index.html | 4 +- .../async_get_thread_context.html | 4 +- .../get_thread_context.html | 4 +- .../context/get_thread_context/index.html | 4 +- docs/reference/context/index.html | 4 +- .../context/respond/async_respond.html | 4 +- docs/reference/context/respond/index.html | 4 +- docs/reference/context/respond/internals.html | 4 +- docs/reference/context/respond/respond.html | 4 +- .../async_save_thread_context.html | 4 +- .../context/save_thread_context/index.html | 4 +- .../save_thread_context.html | 4 +- docs/reference/context/say/async_say.html | 4 +- docs/reference/context/say/index.html | 4 +- docs/reference/context/say/internals.html | 4 +- docs/reference/context/say/say.html | 4 +- .../context/set_status/async_set_status.html | 4 +- docs/reference/context/set_status/index.html | 4 +- .../context/set_status/set_status.html | 4 +- .../async_set_suggested_prompts.html | 4 +- .../context/set_suggested_prompts/index.html | 4 +- .../set_suggested_prompts.html | 4 +- .../context/set_title/async_set_title.html | 4 +- docs/reference/context/set_title/index.html | 4 +- .../context/set_title/set_title.html | 4 +- docs/reference/error/index.html | 4 +- docs/reference/index.html | 145 ++++++++++-------- docs/reference/kwargs_injection/args.html | 4 +- .../kwargs_injection/async_args.html | 4 +- .../kwargs_injection/async_utils.html | 4 +- docs/reference/kwargs_injection/index.html | 4 +- docs/reference/kwargs_injection/utils.html | 4 +- .../lazy_listener/async_internals.html | 4 +- .../reference/lazy_listener/async_runner.html | 4 +- .../lazy_listener/asyncio_runner.html | 4 +- docs/reference/lazy_listener/index.html | 4 +- docs/reference/lazy_listener/internals.html | 4 +- docs/reference/lazy_listener/runner.html | 4 +- .../lazy_listener/thread_runner.html | 4 +- docs/reference/listener/async_builtins.html | 4 +- docs/reference/listener/async_listener.html | 26 +++- .../async_listener_completion_handler.html | 4 +- .../async_listener_error_handler.html | 4 +- .../async_listener_start_handler.html | 4 +- docs/reference/listener/asyncio_runner.html | 8 +- docs/reference/listener/builtins.html | 4 +- docs/reference/listener/custom_listener.html | 10 +- docs/reference/listener/index.html | 16 +- docs/reference/listener/listener.html | 10 +- .../listener/listener_completion_handler.html | 4 +- .../listener/listener_error_handler.html | 4 +- .../listener/listener_start_handler.html | 4 +- docs/reference/listener/thread_runner.html | 12 +- .../listener_matcher/async_builtins.html | 4 +- .../async_listener_matcher.html | 4 +- docs/reference/listener_matcher/builtins.html | 6 +- .../custom_listener_matcher.html | 4 +- docs/reference/listener_matcher/index.html | 4 +- .../listener_matcher/listener_matcher.html | 4 +- docs/reference/logger/index.html | 4 +- docs/reference/logger/messages.html | 19 ++- .../middleware/assistant/assistant.html | 4 +- .../middleware/assistant/async_assistant.html | 4 +- .../reference/middleware/assistant/index.html | 4 +- docs/reference/middleware/async_builtins.html | 16 +- .../middleware/async_custom_middleware.html | 4 +- .../middleware/async_middleware.html | 4 +- .../async_middleware_error_handler.html | 4 +- .../async_attaching_function_token.html | 4 +- .../attaching_function_token.html | 4 +- .../attaching_function_token/index.html | 4 +- .../authorization/async_authorization.html | 4 +- .../authorization/async_internals.html | 4 +- .../async_multi_teams_authorization.html | 4 +- .../async_single_team_authorization.html | 4 +- .../authorization/authorization.html | 4 +- .../middleware/authorization/index.html | 4 +- .../middleware/authorization/internals.html | 4 +- .../multi_teams_authorization.html | 4 +- .../single_team_authorization.html | 4 +- .../middleware/custom_middleware.html | 4 +- .../async_ignoring_self_events.html | 4 +- .../ignoring_self_events.html | 4 +- .../ignoring_self_events/index.html | 4 +- docs/reference/middleware/index.html | 20 +-- .../async_message_listener_matches.html | 4 +- .../message_listener_matches/index.html | 4 +- .../message_listener_matches.html | 4 +- docs/reference/middleware/middleware.html | 4 +- .../middleware/middleware_error_handler.html | 4 +- .../async_request_verification.html | 10 +- .../request_verification/index.html | 8 +- .../request_verification.html | 8 +- .../middleware/ssl_check/async_ssl_check.html | 8 +- .../reference/middleware/ssl_check/index.html | 12 +- .../middleware/ssl_check/ssl_check.html | 12 +- .../async_url_verification.html | 6 +- .../middleware/url_verification/index.html | 8 +- .../url_verification/url_verification.html | 8 +- .../oauth/async_callback_options.html | 4 +- docs/reference/oauth/async_internals.html | 4 +- docs/reference/oauth/async_oauth_flow.html | 4 +- .../reference/oauth/async_oauth_settings.html | 4 +- docs/reference/oauth/callback_options.html | 4 +- docs/reference/oauth/index.html | 4 +- docs/reference/oauth/internals.html | 4 +- docs/reference/oauth/oauth_flow.html | 4 +- docs/reference/oauth/oauth_settings.html | 4 +- docs/reference/request/async_internals.html | 4 +- docs/reference/request/async_request.html | 4 +- docs/reference/request/index.html | 6 +- docs/reference/request/internals.html | 4 +- docs/reference/request/payload_utils.html | 4 +- docs/reference/request/request.html | 4 +- docs/reference/response/index.html | 6 +- docs/reference/response/response.html | 4 +- docs/reference/util/async_utils.html | 4 +- docs/reference/util/index.html | 4 +- docs/reference/util/utils.html | 4 +- docs/reference/version.html | 4 +- docs/reference/workflows/index.html | 6 +- docs/reference/workflows/step/async_step.html | 44 +++--- .../workflows/step/async_step_middleware.html | 4 +- docs/reference/workflows/step/index.html | 28 ++-- docs/reference/workflows/step/internals.html | 4 +- docs/reference/workflows/step/step.html | 44 +++--- .../workflows/step/step_middleware.html | 4 +- .../step/utilities/async_complete.html | 8 +- .../step/utilities/async_configure.html | 8 +- .../workflows/step/utilities/async_fail.html | 8 +- .../step/utilities/async_update.html | 8 +- .../workflows/step/utilities/complete.html | 8 +- .../workflows/step/utilities/configure.html | 8 +- .../workflows/step/utilities/fail.html | 8 +- .../workflows/step/utilities/index.html | 4 +- .../workflows/step/utilities/update.html | 8 +- scripts/generate_api_docs.sh | 6 +- slack_bolt/version.py | 2 +- 231 files changed, 1026 insertions(+), 884 deletions(-) diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index 85b4e13be..69026d602 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -157,10 +157,12 @@ password: {your password} - Commit with a message including the new version number. For example `1.2.3` & Push the commit to a branch and create a PR to sanity check. - `git checkout -b v1.2.3` - `git commit -a -m 'version 1.2.3'` + - `git push -u origin HEAD` - Open a PR and merge after receiving at least one approval from other maintainers. 2. Distribute the release - Use the latest stable Python runtime + - `git checkout main && git pull` - `python --version` - `python -m venv .venv` - `./scripts/deploy_to_pypi_org.sh` diff --git a/docs/reference/adapter/aiohttp/index.html b/docs/reference/adapter/aiohttp/index.html index 879b7a023..7d7ceedbe 100644 --- a/docs/reference/adapter/aiohttp/index.html +++ b/docs/reference/adapter/aiohttp/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aiohttp API documentation @@ -122,7 +122,7 @@

    Functions

    diff --git a/docs/reference/adapter/asgi/aiohttp/index.html b/docs/reference/adapter/asgi/aiohttp/index.html index d598dc6cb..a6aa7c92d 100644 --- a/docs/reference/adapter/asgi/aiohttp/index.html +++ b/docs/reference/adapter/asgi/aiohttp/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi.aiohttp API documentation @@ -158,7 +158,7 @@

    diff --git a/docs/reference/adapter/asgi/async_handler.html b/docs/reference/adapter/asgi/async_handler.html index 9ecdb6fd3..23433ffce 100644 --- a/docs/reference/adapter/asgi/async_handler.html +++ b/docs/reference/adapter/asgi/async_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi.async_handler API documentation @@ -158,7 +158,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/asgi/base_handler.html b/docs/reference/adapter/asgi/base_handler.html index 2e194b12b..b8a6da68f 100644 --- a/docs/reference/adapter/asgi/base_handler.html +++ b/docs/reference/adapter/asgi/base_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi.base_handler API documentation @@ -204,7 +204,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/asgi/builtin/index.html b/docs/reference/adapter/asgi/builtin/index.html index 4a0d0c777..9147380c5 100644 --- a/docs/reference/adapter/asgi/builtin/index.html +++ b/docs/reference/adapter/asgi/builtin/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi.builtin API documentation @@ -159,7 +159,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/asgi/http_request.html b/docs/reference/adapter/asgi/http_request.html index 38ab574da..062ac7ca2 100644 --- a/docs/reference/adapter/asgi/http_request.html +++ b/docs/reference/adapter/asgi/http_request.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi.http_request API documentation @@ -250,7 +250,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/asgi/http_response.html b/docs/reference/adapter/asgi/http_response.html index 93c929224..86e368f6e 100644 --- a/docs/reference/adapter/asgi/http_response.html +++ b/docs/reference/adapter/asgi/http_response.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi.http_response API documentation @@ -252,7 +252,7 @@

    diff --git a/docs/reference/adapter/asgi/index.html b/docs/reference/adapter/asgi/index.html index 295ead704..0f2abec74 100644 --- a/docs/reference/adapter/asgi/index.html +++ b/docs/reference/adapter/asgi/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi API documentation @@ -201,7 +201,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/asgi/utils.html b/docs/reference/adapter/asgi/utils.html index 031deda6c..8eb2a24f1 100644 --- a/docs/reference/adapter/asgi/utils.html +++ b/docs/reference/adapter/asgi/utils.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.asgi.utils API documentation @@ -60,7 +60,7 @@

    Module slack_bolt.adapter.asgi.utils

    diff --git a/docs/reference/adapter/aws_lambda/chalice_handler.html b/docs/reference/adapter/aws_lambda/chalice_handler.html index e8bd162ec..28c75ea6a 100644 --- a/docs/reference/adapter/aws_lambda/chalice_handler.html +++ b/docs/reference/adapter/aws_lambda/chalice_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda.chalice_handler API documentation @@ -278,7 +278,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/aws_lambda/chalice_lazy_listener_runner.html b/docs/reference/adapter/aws_lambda/chalice_lazy_listener_runner.html index 8bd6428c1..f27e09c93 100644 --- a/docs/reference/adapter/aws_lambda/chalice_lazy_listener_runner.html +++ b/docs/reference/adapter/aws_lambda/chalice_lazy_listener_runner.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda.chalice_lazy_listener_runner API documentation @@ -124,7 +124,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/aws_lambda/handler.html b/docs/reference/adapter/aws_lambda/handler.html index 48fd279c2..08e4ac9b7 100644 --- a/docs/reference/adapter/aws_lambda/handler.html +++ b/docs/reference/adapter/aws_lambda/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda.handler API documentation @@ -281,7 +281,7 @@

    diff --git a/docs/reference/adapter/aws_lambda/index.html b/docs/reference/adapter/aws_lambda/index.html index 023c9b6eb..0aae2c31a 100644 --- a/docs/reference/adapter/aws_lambda/index.html +++ b/docs/reference/adapter/aws_lambda/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda API documentation @@ -249,7 +249,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/aws_lambda/internals.html b/docs/reference/adapter/aws_lambda/internals.html index 94fa2be46..bbbe281b0 100644 --- a/docs/reference/adapter/aws_lambda/internals.html +++ b/docs/reference/adapter/aws_lambda/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda.internals API documentation @@ -60,7 +60,7 @@

    Module slack_bolt.adapter.aws_lambda.internals diff --git a/docs/reference/adapter/aws_lambda/lambda_s3_oauth_flow.html b/docs/reference/adapter/aws_lambda/lambda_s3_oauth_flow.html index 067ad846d..11845c902 100644 --- a/docs/reference/adapter/aws_lambda/lambda_s3_oauth_flow.html +++ b/docs/reference/adapter/aws_lambda/lambda_s3_oauth_flow.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow API documentation @@ -204,7 +204,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/aws_lambda/lazy_listener_runner.html b/docs/reference/adapter/aws_lambda/lazy_listener_runner.html index 4fbebcd4e..df53f5f22 100644 --- a/docs/reference/adapter/aws_lambda/lazy_listener_runner.html +++ b/docs/reference/adapter/aws_lambda/lazy_listener_runner.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda.lazy_listener_runner API documentation @@ -116,7 +116,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/aws_lambda/local_lambda_client.html b/docs/reference/adapter/aws_lambda/local_lambda_client.html index 1441fb2b7..45ee0510b 100644 --- a/docs/reference/adapter/aws_lambda/local_lambda_client.html +++ b/docs/reference/adapter/aws_lambda/local_lambda_client.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.aws_lambda.local_lambda_client API documentation @@ -134,7 +134,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/bottle/handler.html b/docs/reference/adapter/bottle/handler.html index e14b2da44..fe6f8ae1a 100644 --- a/docs/reference/adapter/bottle/handler.html +++ b/docs/reference/adapter/bottle/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.bottle.handler API documentation @@ -186,7 +186,7 @@

    diff --git a/docs/reference/adapter/bottle/index.html b/docs/reference/adapter/bottle/index.html index 0ceecb7f7..f240d52bc 100644 --- a/docs/reference/adapter/bottle/index.html +++ b/docs/reference/adapter/bottle/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.bottle API documentation @@ -153,7 +153,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/cherrypy/handler.html b/docs/reference/adapter/cherrypy/handler.html index 735786296..d41f00148 100644 --- a/docs/reference/adapter/cherrypy/handler.html +++ b/docs/reference/adapter/cherrypy/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.cherrypy.handler API documentation @@ -228,7 +228,7 @@

    diff --git a/docs/reference/adapter/cherrypy/index.html b/docs/reference/adapter/cherrypy/index.html index a1c121e05..5a322fd7a 100644 --- a/docs/reference/adapter/cherrypy/index.html +++ b/docs/reference/adapter/cherrypy/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.cherrypy API documentation @@ -157,7 +157,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/django/handler.html b/docs/reference/adapter/django/handler.html index a2de8c99d..4fe9e359a 100644 --- a/docs/reference/adapter/django/handler.html +++ b/docs/reference/adapter/django/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.django.handler API documentation @@ -382,7 +382,7 @@

    diff --git a/docs/reference/adapter/django/index.html b/docs/reference/adapter/django/index.html index ed9f43658..dfb6af63f 100644 --- a/docs/reference/adapter/django/index.html +++ b/docs/reference/adapter/django/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.django API documentation @@ -194,7 +194,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/falcon/async_resource.html b/docs/reference/adapter/falcon/async_resource.html index 07d432390..f43ab11ef 100644 --- a/docs/reference/adapter/falcon/async_resource.html +++ b/docs/reference/adapter/falcon/async_resource.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.falcon.async_resource API documentation @@ -87,7 +87,7 @@

    Classes

    resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment] + resp.body = "The page is not found..." async def on_post(self, req: Request, resp: Response): bolt_req = await self._to_bolt_request(req) @@ -151,7 +151,7 @@

    Methods

    resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment]
    + resp.body = "The page is not found..."
    @@ -200,7 +200,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/falcon/index.html b/docs/reference/adapter/falcon/index.html index 50874f7b8..82a2a57e2 100644 --- a/docs/reference/adapter/falcon/index.html +++ b/docs/reference/adapter/falcon/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.falcon API documentation @@ -93,7 +93,7 @@

    Classes

    resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment] + resp.body = "The page is not found..." def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -110,7 +110,7 @@

    Classes

    def _write_response(self, bolt_resp: BoltResponse, resp: Response): if falcon_version.__version__.startswith("2."): # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = bolt_resp.body # type: ignore[assignment] + resp.body = bolt_resp.body else: resp.text = bolt_resp.body @@ -161,7 +161,7 @@

    Methods

    resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment]
    + resp.body = "The page is not found..."
    @@ -216,7 +216,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/falcon/resource.html b/docs/reference/adapter/falcon/resource.html index b999b290c..73860adc1 100644 --- a/docs/reference/adapter/falcon/resource.html +++ b/docs/reference/adapter/falcon/resource.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.falcon.resource API documentation @@ -82,7 +82,7 @@

    Classes

    resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment] + resp.body = "The page is not found..." def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -99,7 +99,7 @@

    Classes

    def _write_response(self, bolt_resp: BoltResponse, resp: Response): if falcon_version.__version__.startswith("2."): # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = bolt_resp.body # type: ignore[assignment] + resp.body = bolt_resp.body else: resp.text = bolt_resp.body @@ -150,7 +150,7 @@

    Methods

    resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." # type: ignore[assignment]
    + resp.body = "The page is not found..."
    @@ -199,7 +199,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/fastapi/async_handler.html b/docs/reference/adapter/fastapi/async_handler.html index 0056ce305..6f6205e51 100644 --- a/docs/reference/adapter/fastapi/async_handler.html +++ b/docs/reference/adapter/fastapi/async_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.fastapi.async_handler API documentation @@ -149,7 +149,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/fastapi/index.html b/docs/reference/adapter/fastapi/index.html index 49b91a814..6ffb52f35 100644 --- a/docs/reference/adapter/fastapi/index.html +++ b/docs/reference/adapter/fastapi/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.fastapi API documentation @@ -153,7 +153,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/flask/handler.html b/docs/reference/adapter/flask/handler.html index 1b3952603..489b80a90 100644 --- a/docs/reference/adapter/flask/handler.html +++ b/docs/reference/adapter/flask/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.flask.handler API documentation @@ -179,7 +179,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/flask/index.html b/docs/reference/adapter/flask/index.html index 7d4b60292..ee765fa1e 100644 --- a/docs/reference/adapter/flask/index.html +++ b/docs/reference/adapter/flask/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.flask API documentation @@ -145,7 +145,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/google_cloud_functions/handler.html b/docs/reference/adapter/google_cloud_functions/handler.html index 0b48a26a4..1d9b0da7f 100644 --- a/docs/reference/adapter/google_cloud_functions/handler.html +++ b/docs/reference/adapter/google_cloud_functions/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.google_cloud_functions.handler API documentation @@ -170,7 +170,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/google_cloud_functions/index.html b/docs/reference/adapter/google_cloud_functions/index.html index 3ad305b8d..790d210be 100644 --- a/docs/reference/adapter/google_cloud_functions/index.html +++ b/docs/reference/adapter/google_cloud_functions/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.google_cloud_functions API documentation @@ -147,7 +147,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/index.html b/docs/reference/adapter/index.html index 78ef4a2f7..646c0ac81 100644 --- a/docs/reference/adapter/index.html +++ b/docs/reference/adapter/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter API documentation @@ -148,7 +148,7 @@

    Sub-modules

    diff --git a/docs/reference/adapter/pyramid/handler.html b/docs/reference/adapter/pyramid/handler.html index 2f26bbc38..4a4a68849 100644 --- a/docs/reference/adapter/pyramid/handler.html +++ b/docs/reference/adapter/pyramid/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.pyramid.handler API documentation @@ -195,7 +195,7 @@

    diff --git a/docs/reference/adapter/pyramid/index.html b/docs/reference/adapter/pyramid/index.html index 30d3685e8..7f0903cb6 100644 --- a/docs/reference/adapter/pyramid/index.html +++ b/docs/reference/adapter/pyramid/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.pyramid API documentation @@ -151,7 +151,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/sanic/async_handler.html b/docs/reference/adapter/sanic/async_handler.html index 37945775c..adabe53be 100644 --- a/docs/reference/adapter/sanic/async_handler.html +++ b/docs/reference/adapter/sanic/async_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.sanic.async_handler API documentation @@ -210,7 +210,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/sanic/index.html b/docs/reference/adapter/sanic/index.html index 1ae23450c..558bb321c 100644 --- a/docs/reference/adapter/sanic/index.html +++ b/docs/reference/adapter/sanic/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.sanic API documentation @@ -153,7 +153,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/socket_mode/aiohttp/index.html b/docs/reference/adapter/socket_mode/aiohttp/index.html index f9d240874..cc91a3d06 100644 --- a/docs/reference/adapter/socket_mode/aiohttp/index.html +++ b/docs/reference/adapter/socket_mode/aiohttp/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.aiohttp API documentation @@ -239,7 +239,7 @@

    diff --git a/docs/reference/adapter/socket_mode/async_base_handler.html b/docs/reference/adapter/socket_mode/async_base_handler.html index 31b681b3c..b00420c11 100644 --- a/docs/reference/adapter/socket_mode/async_base_handler.html +++ b/docs/reference/adapter/socket_mode/async_base_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.async_base_handler API documentation @@ -240,7 +240,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/socket_mode/async_handler.html b/docs/reference/adapter/socket_mode/async_handler.html index 5093f1281..447ecf0ea 100644 --- a/docs/reference/adapter/socket_mode/async_handler.html +++ b/docs/reference/adapter/socket_mode/async_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.async_handler API documentation @@ -142,7 +142,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/socket_mode/async_internals.html b/docs/reference/adapter/socket_mode/async_internals.html index 5b7769483..d2e300efa 100644 --- a/docs/reference/adapter/socket_mode/async_internals.html +++ b/docs/reference/adapter/socket_mode/async_internals.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.async_internals API documentation @@ -121,7 +121,7 @@

    Functions

    diff --git a/docs/reference/adapter/socket_mode/base_handler.html b/docs/reference/adapter/socket_mode/base_handler.html index b57156928..450f9ac0e 100644 --- a/docs/reference/adapter/socket_mode/base_handler.html +++ b/docs/reference/adapter/socket_mode/base_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.base_handler API documentation @@ -252,7 +252,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/socket_mode/builtin/index.html b/docs/reference/adapter/socket_mode/builtin/index.html index ab1837ae3..fc66eb203 100644 --- a/docs/reference/adapter/socket_mode/builtin/index.html +++ b/docs/reference/adapter/socket_mode/builtin/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.builtin API documentation @@ -200,7 +200,7 @@

    diff --git a/docs/reference/adapter/socket_mode/index.html b/docs/reference/adapter/socket_mode/index.html index cb26a212a..511ef4840 100644 --- a/docs/reference/adapter/socket_mode/index.html +++ b/docs/reference/adapter/socket_mode/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode API documentation @@ -260,7 +260,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/socket_mode/internals.html b/docs/reference/adapter/socket_mode/internals.html index 7c1a7a81f..55d96b054 100644 --- a/docs/reference/adapter/socket_mode/internals.html +++ b/docs/reference/adapter/socket_mode/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.internals API documentation @@ -119,7 +119,7 @@

    Functions

    diff --git a/docs/reference/adapter/socket_mode/websocket_client/index.html b/docs/reference/adapter/socket_mode/websocket_client/index.html index d6a4b50b4..e837ef19b 100644 --- a/docs/reference/adapter/socket_mode/websocket_client/index.html +++ b/docs/reference/adapter/socket_mode/websocket_client/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.websocket_client API documentation @@ -190,7 +190,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/socket_mode/websockets/index.html b/docs/reference/adapter/socket_mode/websockets/index.html index 2b7e9f493..7f96f0021 100644 --- a/docs/reference/adapter/socket_mode/websockets/index.html +++ b/docs/reference/adapter/socket_mode/websockets/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.socket_mode.websockets API documentation @@ -239,7 +239,7 @@

    diff --git a/docs/reference/adapter/starlette/async_handler.html b/docs/reference/adapter/starlette/async_handler.html index e8e596f2a..91345eba3 100644 --- a/docs/reference/adapter/starlette/async_handler.html +++ b/docs/reference/adapter/starlette/async_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.starlette.async_handler API documentation @@ -213,7 +213,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/starlette/handler.html b/docs/reference/adapter/starlette/handler.html index 5171240fd..5c74b71da 100644 --- a/docs/reference/adapter/starlette/handler.html +++ b/docs/reference/adapter/starlette/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.starlette.handler API documentation @@ -205,7 +205,7 @@

    diff --git a/docs/reference/adapter/starlette/index.html b/docs/reference/adapter/starlette/index.html index 3af382537..bdf5bf42a 100644 --- a/docs/reference/adapter/starlette/index.html +++ b/docs/reference/adapter/starlette/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.starlette API documentation @@ -158,7 +158,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/tornado/async_handler.html b/docs/reference/adapter/tornado/async_handler.html index b7d813420..c274429de 100644 --- a/docs/reference/adapter/tornado/async_handler.html +++ b/docs/reference/adapter/tornado/async_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.tornado.async_handler API documentation @@ -242,7 +242,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/tornado/handler.html b/docs/reference/adapter/tornado/handler.html index 1149311a9..a69adb987 100644 --- a/docs/reference/adapter/tornado/handler.html +++ b/docs/reference/adapter/tornado/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.tornado.handler API documentation @@ -273,7 +273,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/tornado/index.html b/docs/reference/adapter/tornado/index.html index dac1b6b78..a5bec4ffb 100644 --- a/docs/reference/adapter/tornado/index.html +++ b/docs/reference/adapter/tornado/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.tornado API documentation @@ -234,7 +234,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/wsgi/handler.html b/docs/reference/adapter/wsgi/handler.html index 8369cb636..204499a05 100644 --- a/docs/reference/adapter/wsgi/handler.html +++ b/docs/reference/adapter/wsgi/handler.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.wsgi.handler API documentation @@ -230,7 +230,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/wsgi/http_request.html b/docs/reference/adapter/wsgi/http_request.html index c4495e440..fa845dd93 100644 --- a/docs/reference/adapter/wsgi/http_request.html +++ b/docs/reference/adapter/wsgi/http_request.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.wsgi.http_request API documentation @@ -373,7 +373,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/wsgi/http_response.html b/docs/reference/adapter/wsgi/http_response.html index 3ddc2350c..da7dc33f0 100644 --- a/docs/reference/adapter/wsgi/http_response.html +++ b/docs/reference/adapter/wsgi/http_response.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.wsgi.http_response API documentation @@ -191,7 +191,7 @@

    diff --git a/docs/reference/adapter/wsgi/index.html b/docs/reference/adapter/wsgi/index.html index 49ab0d930..c3cfafea1 100644 --- a/docs/reference/adapter/wsgi/index.html +++ b/docs/reference/adapter/wsgi/index.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.wsgi API documentation @@ -257,7 +257,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/adapter/wsgi/internals.html b/docs/reference/adapter/wsgi/internals.html index addbae583..7fdfa267f 100644 --- a/docs/reference/adapter/wsgi/internals.html +++ b/docs/reference/adapter/wsgi/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.adapter.wsgi.internals API documentation @@ -60,7 +60,7 @@

    Module slack_bolt.adapter.wsgi.internals

    diff --git a/docs/reference/app/app.html b/docs/reference/app/app.html index 02fc5b036..d1224dd5d 100644 --- a/docs/reference/app/app.html +++ b/docs/reference/app/app.html @@ -3,7 +3,7 @@ - + slack_bolt.app.app API documentation @@ -675,7 +675,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -693,7 +693,7 @@

    Classes

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -710,7 +710,7 @@

    Classes

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -787,7 +787,7 @@

    Classes

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -825,7 +825,7 @@

    Classes

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -871,6 +871,7 @@

    Classes

    matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -899,13 +900,17 @@

    Classes

    Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) + matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ @@ -931,7 +936,7 @@

    Classes

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -978,7 +983,7 @@

    Classes

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1046,9 +1051,9 @@

    Classes

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1074,7 +1079,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1091,7 +1096,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1107,7 +1112,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1123,7 +1128,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1164,7 +1169,7 @@

    Classes

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1190,7 +1195,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1206,7 +1211,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1247,7 +1252,8 @@

    Classes

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1287,7 +1293,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1380,6 +1386,7 @@

    Classes

    matchers: Optional[Sequence[Callable[..., bool]]], middleware: Optional[Sequence[Union[Callable, Middleware]]], auto_acknowledgement: bool = False, + ack_timeout: int = 3, ) -> Optional[Callable[..., Optional[BoltResponse]]]: value_to_return = None if not isinstance(functions, list): @@ -1406,10 +1413,11 @@

    Classes

    CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, + lazy_functions=functions, # type:ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, + ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) @@ -1629,9 +1637,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1660,9 +1668,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1705,7 +1713,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1715,7 +1723,7 @@

    Args

    return __call__

    Registers a new interactive_message action listener. -Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.

    +Refer to https://api.slack.com/legacy/message-buttons for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1732,7 +1740,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1743,7 +1751,7 @@

    Args

    return __call__

    Registers a new block_actions action listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1797,7 +1805,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1828,7 +1836,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands.

    +

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1891,7 +1899,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1901,7 +1909,7 @@

    Args

    return __call__

    Registers a new dialog_cancellation listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1918,7 +1926,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1928,7 +1936,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1945,7 +1953,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1955,7 +1963,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dispatch(self,
    req: BoltRequest) ‑> BoltResponse
    @@ -2181,7 +2189,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2213,7 +2221,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    +

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2229,7 +2237,7 @@

    Args

    -def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None,
    auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    +def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None,
    auto_acknowledge: bool = True,
    ack_timeout: int = 3) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -2242,6 +2250,7 @@

    Args

    matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -2270,13 +2279,17 @@

    Args

    Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) + matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__
    @@ -2360,7 +2373,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2411,7 +2424,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://docs.slack.dev/reference/events/message for details of message events.

    +

    Refer to https://api.slack.com/events/message for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2554,7 +2567,8 @@

    Args

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2594,7 +2608,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2640,7 +2655,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2677,7 +2692,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts.

    +

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2763,7 +2778,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -2781,7 +2796,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2798,7 +2813,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -2820,7 +2835,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use WorkflowStepBuilder's methods.

    @@ -2835,7 +2850,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    +

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    For further information about WorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -2906,7 +2921,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2947,7 +2962,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    +

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2976,7 +2991,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2986,7 +3001,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -3003,7 +3018,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3013,7 +3028,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    @@ -3264,7 +3279,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/app/async_app.html b/docs/reference/app/async_app.html index 66af8f038..b6634710a 100644 --- a/docs/reference/app/async_app.html +++ b/docs/reference/app/async_app.html @@ -3,7 +3,7 @@ - + slack_bolt.app.async_app API documentation @@ -687,7 +687,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -705,7 +705,7 @@

    Classes

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -721,7 +721,7 @@

    Classes

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -803,7 +803,7 @@

    Classes

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -841,7 +841,7 @@

    Classes

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -890,6 +890,7 @@

    Classes

    matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -917,6 +918,9 @@

    Classes

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] @@ -926,7 +930,7 @@

    Classes

    primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ @@ -952,7 +956,7 @@

    Classes

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -999,7 +1003,7 @@

    Classes

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1067,9 +1071,9 @@

    Classes

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1095,7 +1099,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1112,7 +1116,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1128,7 +1132,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1144,7 +1148,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1185,7 +1189,7 @@

    Classes

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1211,7 +1215,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1227,7 +1231,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1268,7 +1272,8 @@

    Classes

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1308,7 +1313,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1405,6 +1410,7 @@

    Classes

    matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]], middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]], auto_acknowledgement: bool = False, + ack_timeout: int = 3, ) -> Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]: value_to_return = None if not isinstance(functions, list): @@ -1436,10 +1442,11 @@

    Classes

    AsyncCustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, + lazy_functions=functions, # type:ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, + ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) @@ -1662,9 +1669,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1693,9 +1700,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -1866,7 +1873,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1876,7 +1883,7 @@

    Returns

    return __call__

    Registers a new interactive_message action listener. -Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.

    +Refer to https://api.slack.com/legacy/message-buttons for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -1893,7 +1900,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1904,7 +1911,7 @@

    Returns

    return __call__

    Registers a new block_actions action listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -1958,7 +1965,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1989,7 +1996,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands.

    +

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2052,7 +2059,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2062,7 +2069,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2079,7 +2086,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2089,7 +2096,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2106,7 +2113,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2116,7 +2123,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def enable_token_revocation_listeners(self) ‑> None @@ -2222,7 +2229,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2254,7 +2261,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    +

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2270,7 +2277,7 @@

    Args

    -def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None,
    auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., Awaitable[BoltResponse]] | None]
    +def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None,
    auto_acknowledge: bool = True,
    ack_timeout: int = 3) ‑> Callable[..., Callable[..., Awaitable[BoltResponse]] | None]
    @@ -2283,6 +2290,7 @@

    Args

    matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -2310,6 +2318,9 @@

    Args

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] @@ -2319,7 +2330,7 @@

    Args

    primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__
    @@ -2403,7 +2414,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2457,7 +2468,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://docs.slack.dev/reference/events/message for details of message events.

    +

    Refer to https://api.slack.com/events/message for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2597,7 +2608,8 @@

    Args

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2637,7 +2649,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2726,7 +2739,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2763,7 +2776,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts.

    +

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2826,7 +2839,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -2844,7 +2857,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -2860,7 +2873,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -2882,7 +2895,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use AsyncWorkflowStepBuilder's methods.

    @@ -2897,7 +2910,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    +

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document. For further information about AsyncWorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -2965,7 +2978,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -3006,7 +3019,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    +

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -3035,7 +3048,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3045,7 +3058,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -3062,7 +3075,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3072,7 +3085,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    def web_app(self, path: str = '/slack/events', port: int = 3000) ‑> aiohttp.web_app.Application @@ -3192,7 +3205,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/app/async_server.html b/docs/reference/app/async_server.html index 25d28fed1..b95b8a2b3 100644 --- a/docs/reference/app/async_server.html +++ b/docs/reference/app/async_server.html @@ -3,7 +3,7 @@ - + slack_bolt.app.async_server API documentation @@ -270,7 +270,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/app/index.html b/docs/reference/app/index.html index f1a7e9655..857fb22c8 100644 --- a/docs/reference/app/index.html +++ b/docs/reference/app/index.html @@ -3,7 +3,7 @@ - + slack_bolt.app API documentation @@ -694,7 +694,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -712,7 +712,7 @@

    Classes

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -729,7 +729,7 @@

    Classes

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -806,7 +806,7 @@

    Classes

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -844,7 +844,7 @@

    Classes

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -890,6 +890,7 @@

    Classes

    matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -918,13 +919,17 @@

    Classes

    Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) + matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ @@ -950,7 +955,7 @@

    Classes

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -997,7 +1002,7 @@

    Classes

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1065,9 +1070,9 @@

    Classes

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1093,7 +1098,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1110,7 +1115,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1126,7 +1131,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1142,7 +1147,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1183,7 +1188,7 @@

    Classes

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1209,7 +1214,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1225,7 +1230,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1266,7 +1271,8 @@

    Classes

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1306,7 +1312,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1399,6 +1405,7 @@

    Classes

    matchers: Optional[Sequence[Callable[..., bool]]], middleware: Optional[Sequence[Union[Callable, Middleware]]], auto_acknowledgement: bool = False, + ack_timeout: int = 3, ) -> Optional[Callable[..., Optional[BoltResponse]]]: value_to_return = None if not isinstance(functions, list): @@ -1425,10 +1432,11 @@

    Classes

    CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, + lazy_functions=functions, # type:ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, + ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) @@ -1648,9 +1656,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1679,9 +1687,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1724,7 +1732,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1734,7 +1742,7 @@

    Args

    return __call__

    Registers a new interactive_message action listener. -Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.

    +Refer to https://api.slack.com/legacy/message-buttons for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1751,7 +1759,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1762,7 +1770,7 @@

    Args

    return __call__

    Registers a new block_actions action listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1816,7 +1824,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1847,7 +1855,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands.

    +

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1910,7 +1918,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1920,7 +1928,7 @@

    Args

    return __call__

    Registers a new dialog_cancellation listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1937,7 +1945,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1947,7 +1955,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1964,7 +1972,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1974,7 +1982,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dispatch(self,
    req: BoltRequest) ‑> BoltResponse
    @@ -2200,7 +2208,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2232,7 +2240,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    +

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2248,7 +2256,7 @@

    Args

    -def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None,
    auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    +def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None,
    auto_acknowledge: bool = True,
    ack_timeout: int = 3) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -2261,6 +2269,7 @@

    Args

    matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -2289,13 +2298,17 @@

    Args

    Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) + matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__
    @@ -2379,7 +2392,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2430,7 +2443,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://docs.slack.dev/reference/events/message for details of message events.

    +

    Refer to https://api.slack.com/events/message for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2573,7 +2586,8 @@

    Args

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2613,7 +2627,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2659,7 +2674,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2696,7 +2711,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts.

    +

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2782,7 +2797,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -2800,7 +2815,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2817,7 +2832,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -2839,7 +2854,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use WorkflowStepBuilder's methods.

    @@ -2854,7 +2869,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    +

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    For further information about WorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -2925,7 +2940,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2966,7 +2981,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    +

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2995,7 +3010,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3005,7 +3020,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -3022,7 +3037,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3032,7 +3047,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    @@ -3104,7 +3119,7 @@

    App diff --git a/docs/reference/async_app.html b/docs/reference/async_app.html index c067aeb5e..07c1f3627 100644 --- a/docs/reference/async_app.html +++ b/docs/reference/async_app.html @@ -3,7 +3,7 @@ - + slack_bolt.async_app API documentation @@ -778,7 +778,7 @@

    Class variables

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -796,7 +796,7 @@

    Class variables

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -812,7 +812,7 @@

    Class variables

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -894,7 +894,7 @@

    Class variables

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -932,7 +932,7 @@

    Class variables

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -981,6 +981,7 @@

    Class variables

    matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -1008,6 +1009,9 @@

    Class variables

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] @@ -1017,7 +1021,7 @@

    Class variables

    primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ @@ -1043,7 +1047,7 @@

    Class variables

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1090,7 +1094,7 @@

    Class variables

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1158,9 +1162,9 @@

    Class variables

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1186,7 +1190,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1203,7 +1207,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1219,7 +1223,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1235,7 +1239,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1276,7 +1280,7 @@

    Class variables

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1302,7 +1306,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1318,7 +1322,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1359,7 +1363,8 @@

    Class variables

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1399,7 +1404,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1496,6 +1501,7 @@

    Class variables

    matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]], middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]], auto_acknowledgement: bool = False, + ack_timeout: int = 3, ) -> Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]: value_to_return = None if not isinstance(functions, list): @@ -1527,10 +1533,11 @@

    Class variables

    AsyncCustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, + lazy_functions=functions, # type:ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, + ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) @@ -1753,9 +1760,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1784,9 +1791,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -1957,7 +1964,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1967,7 +1974,7 @@

    Returns

    return __call__

    Registers a new interactive_message action listener. -Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.

    +Refer to https://api.slack.com/legacy/message-buttons for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -1984,7 +1991,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1995,7 +2002,7 @@

    Returns

    return __call__

    Registers a new block_actions action listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2049,7 +2056,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2080,7 +2087,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands.

    +

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2143,7 +2150,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2153,7 +2160,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2170,7 +2177,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2180,7 +2187,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2197,7 +2204,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2207,7 +2214,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def enable_token_revocation_listeners(self) ‑> None @@ -2313,7 +2320,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2345,7 +2352,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    +

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2361,7 +2368,7 @@

    Args

    -def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None,
    auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., Awaitable[BoltResponse]] | None]
    +def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None,
    auto_acknowledge: bool = True,
    ack_timeout: int = 3) ‑> Callable[..., Callable[..., Awaitable[BoltResponse]] | None]
    @@ -2374,6 +2381,7 @@

    Args

    matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]] = None, middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Awaitable[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -2401,6 +2409,9 @@

    Args

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] @@ -2410,7 +2421,7 @@

    Args

    primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__
    @@ -2494,7 +2505,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2548,7 +2559,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://docs.slack.dev/reference/events/message for details of message events.

    +

    Refer to https://api.slack.com/events/message for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2688,7 +2699,8 @@

    Args

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2728,7 +2740,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2817,7 +2830,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2854,7 +2867,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts.

    +

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2917,7 +2930,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -2935,7 +2948,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -2951,7 +2964,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -2973,7 +2986,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use AsyncWorkflowStepBuilder's methods.

    @@ -2988,7 +3001,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    +

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document. For further information about AsyncWorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -3056,7 +3069,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -3097,7 +3110,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    +

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -3126,7 +3139,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3136,7 +3149,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -3153,7 +3166,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3163,7 +3176,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    def web_app(self, path: str = '/slack/events', port: int = 3000) ‑> aiohttp.web_app.Application @@ -4779,6 +4792,7 @@

    Class variables

    ack_function: Callable[..., Awaitable[BoltResponse]] lazy_functions: Sequence[Callable[..., Awaitable[None]]] auto_acknowledgement: bool + ack_timeout: int async def async_matches( self, @@ -4844,6 +4858,10 @@

    Class variables

    The type of the None singleton.

    +
    var ack_timeout : int
    +
    +

    The type of the None singleton.

    +
    var auto_acknowledgement : bool

    The type of the None singleton.

    @@ -5497,6 +5515,7 @@

    AsyncListener

    • ack_function
    • +
    • ack_timeout
    • async_matches
    • auto_acknowledgement
    • lazy_functions
    • @@ -5561,7 +5580,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/authorization/async_authorize.html b/docs/reference/authorization/async_authorize.html index 0a0640780..b4dfa2682 100644 --- a/docs/reference/authorization/async_authorize.html +++ b/docs/reference/authorization/async_authorize.html @@ -3,7 +3,7 @@ - + slack_bolt.authorization.async_authorize API documentation @@ -518,7 +518,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/authorization/async_authorize_args.html b/docs/reference/authorization/async_authorize_args.html index 642b35f93..5de20f757 100644 --- a/docs/reference/authorization/async_authorize_args.html +++ b/docs/reference/authorization/async_authorize_args.html @@ -3,7 +3,7 @@ - + slack_bolt.authorization.async_authorize_args API documentation @@ -158,7 +158,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/authorization/authorize.html b/docs/reference/authorization/authorize.html index 255c87196..33b50be02 100644 --- a/docs/reference/authorization/authorize.html +++ b/docs/reference/authorization/authorize.html @@ -3,7 +3,7 @@ - + slack_bolt.authorization.authorize API documentation @@ -516,7 +516,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/authorization/authorize_args.html b/docs/reference/authorization/authorize_args.html index 660ac17eb..78423fc40 100644 --- a/docs/reference/authorization/authorize_args.html +++ b/docs/reference/authorization/authorize_args.html @@ -3,7 +3,7 @@ - + slack_bolt.authorization.authorize_args API documentation @@ -158,7 +158,7 @@

      diff --git a/docs/reference/authorization/authorize_result.html b/docs/reference/authorization/authorize_result.html index 3bddc0a35..6eac3724d 100644 --- a/docs/reference/authorization/authorize_result.html +++ b/docs/reference/authorization/authorize_result.html @@ -3,7 +3,7 @@ - + slack_bolt.authorization.authorize_result API documentation @@ -292,7 +292,7 @@

      diff --git a/docs/reference/authorization/index.html b/docs/reference/authorization/index.html index eaa267d29..64ca14f0e 100644 --- a/docs/reference/authorization/index.html +++ b/docs/reference/authorization/index.html @@ -3,7 +3,7 @@ - + slack_bolt.authorization API documentation @@ -328,7 +328,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/ack/ack.html b/docs/reference/context/ack/ack.html index e1b71bcb9..a8b808d86 100644 --- a/docs/reference/context/ack/ack.html +++ b/docs/reference/context/ack/ack.html @@ -3,7 +3,7 @@ - + slack_bolt.context.ack.ack API documentation @@ -127,7 +127,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/ack/async_ack.html b/docs/reference/context/ack/async_ack.html index 0f2989e9f..f744d5693 100644 --- a/docs/reference/context/ack/async_ack.html +++ b/docs/reference/context/ack/async_ack.html @@ -3,7 +3,7 @@ - + slack_bolt.context.ack.async_ack API documentation @@ -127,7 +127,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/ack/index.html b/docs/reference/context/ack/index.html index 230da5d99..89f0600e8 100644 --- a/docs/reference/context/ack/index.html +++ b/docs/reference/context/ack/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.ack API documentation @@ -149,7 +149,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/ack/internals.html b/docs/reference/context/ack/internals.html index 2fa3d8028..f7f776241 100644 --- a/docs/reference/context/ack/internals.html +++ b/docs/reference/context/ack/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.context.ack.internals API documentation @@ -60,7 +60,7 @@

      Module slack_bolt.context.ack.internals

      diff --git a/docs/reference/context/assistant/assistant_utilities.html b/docs/reference/context/assistant/assistant_utilities.html index 7bfeeeee9..d446b3c02 100644 --- a/docs/reference/context/assistant/assistant_utilities.html +++ b/docs/reference/context/assistant/assistant_utilities.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.assistant_utilities API documentation @@ -289,7 +289,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/assistant/async_assistant_utilities.html b/docs/reference/context/assistant/async_assistant_utilities.html index b4af582a7..fc3cbbe8b 100644 --- a/docs/reference/context/assistant/async_assistant_utilities.html +++ b/docs/reference/context/assistant/async_assistant_utilities.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.async_assistant_utilities API documentation @@ -283,7 +283,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/assistant/index.html b/docs/reference/context/assistant/index.html index 73dcff282..d442e26cf 100644 --- a/docs/reference/context/assistant/index.html +++ b/docs/reference/context/assistant/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant API documentation @@ -92,7 +92,7 @@

      Sub-modules

      diff --git a/docs/reference/context/assistant/internals.html b/docs/reference/context/assistant/internals.html index b1558b9d2..242bd6f19 100644 --- a/docs/reference/context/assistant/internals.html +++ b/docs/reference/context/assistant/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.internals API documentation @@ -89,7 +89,7 @@

      Functions

      diff --git a/docs/reference/context/assistant/thread_context/index.html b/docs/reference/context/assistant/thread_context/index.html index 7d1232b1d..f3767a1cf 100644 --- a/docs/reference/context/assistant/thread_context/index.html +++ b/docs/reference/context/assistant/thread_context/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.thread_context API documentation @@ -126,7 +126,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/assistant/thread_context_store/async_store.html b/docs/reference/context/assistant/thread_context_store/async_store.html index f5045739f..64f4e53ed 100644 --- a/docs/reference/context/assistant/thread_context_store/async_store.html +++ b/docs/reference/context/assistant/thread_context_store/async_store.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.thread_context_store.async_store API documentation @@ -124,7 +124,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/assistant/thread_context_store/default_async_store.html b/docs/reference/context/assistant/thread_context_store/default_async_store.html index 8344971de..f6cd66060 100644 --- a/docs/reference/context/assistant/thread_context_store/default_async_store.html +++ b/docs/reference/context/assistant/thread_context_store/default_async_store.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.thread_context_store.default_async_store API documentation @@ -190,7 +190,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/assistant/thread_context_store/default_store.html b/docs/reference/context/assistant/thread_context_store/default_store.html index d647b9c78..1594c5d38 100644 --- a/docs/reference/context/assistant/thread_context_store/default_store.html +++ b/docs/reference/context/assistant/thread_context_store/default_store.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.thread_context_store.default_store API documentation @@ -188,7 +188,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/assistant/thread_context_store/file/index.html b/docs/reference/context/assistant/thread_context_store/file/index.html index 4190a948f..4a5d944e1 100644 --- a/docs/reference/context/assistant/thread_context_store/file/index.html +++ b/docs/reference/context/assistant/thread_context_store/file/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.thread_context_store.file API documentation @@ -159,7 +159,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/assistant/thread_context_store/index.html b/docs/reference/context/assistant/thread_context_store/index.html index 400b3c37a..3083275d9 100644 --- a/docs/reference/context/assistant/thread_context_store/index.html +++ b/docs/reference/context/assistant/thread_context_store/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.thread_context_store API documentation @@ -92,7 +92,7 @@

      Sub-modules

      diff --git a/docs/reference/context/assistant/thread_context_store/store.html b/docs/reference/context/assistant/thread_context_store/store.html index fde47afc9..a0a177b09 100644 --- a/docs/reference/context/assistant/thread_context_store/store.html +++ b/docs/reference/context/assistant/thread_context_store/store.html @@ -3,7 +3,7 @@ - + slack_bolt.context.assistant.thread_context_store.store API documentation @@ -125,7 +125,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/async_context.html b/docs/reference/context/async_context.html index 76ac8c5de..9ce4ebd9e 100644 --- a/docs/reference/context/async_context.html +++ b/docs/reference/context/async_context.html @@ -3,7 +3,7 @@ - + slack_bolt.context.async_context API documentation @@ -706,7 +706,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/base_context.html b/docs/reference/context/base_context.html index 54617176b..4a177f8dc 100644 --- a/docs/reference/context/base_context.html +++ b/docs/reference/context/base_context.html @@ -3,7 +3,7 @@ - + slack_bolt.context.base_context API documentation @@ -640,7 +640,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/complete/async_complete.html b/docs/reference/context/complete/async_complete.html index e6ce03d74..36cf1f92f 100644 --- a/docs/reference/context/complete/async_complete.html +++ b/docs/reference/context/complete/async_complete.html @@ -3,7 +3,7 @@ - + slack_bolt.context.complete.async_complete API documentation @@ -127,7 +127,7 @@

      diff --git a/docs/reference/context/complete/complete.html b/docs/reference/context/complete/complete.html index ef6c6c78f..b1f01ea1a 100644 --- a/docs/reference/context/complete/complete.html +++ b/docs/reference/context/complete/complete.html @@ -3,7 +3,7 @@ - + slack_bolt.context.complete.complete API documentation @@ -125,7 +125,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/complete/index.html b/docs/reference/context/complete/index.html index f476fa258..7665622b6 100644 --- a/docs/reference/context/complete/index.html +++ b/docs/reference/context/complete/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.complete API documentation @@ -142,7 +142,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/context.html b/docs/reference/context/context.html index c1ae2789e..615432502 100644 --- a/docs/reference/context/context.html +++ b/docs/reference/context/context.html @@ -3,7 +3,7 @@ - + slack_bolt.context.context API documentation @@ -708,7 +708,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/fail/async_fail.html b/docs/reference/context/fail/async_fail.html index 91497eff9..6b3e4f1df 100644 --- a/docs/reference/context/fail/async_fail.html +++ b/docs/reference/context/fail/async_fail.html @@ -3,7 +3,7 @@ - + slack_bolt.context.fail.async_fail API documentation @@ -125,7 +125,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/fail/fail.html b/docs/reference/context/fail/fail.html index 20d44d1d5..0152561d8 100644 --- a/docs/reference/context/fail/fail.html +++ b/docs/reference/context/fail/fail.html @@ -3,7 +3,7 @@ - + slack_bolt.context.fail.fail API documentation @@ -125,7 +125,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/fail/index.html b/docs/reference/context/fail/index.html index 2b14ac772..eb2653106 100644 --- a/docs/reference/context/fail/index.html +++ b/docs/reference/context/fail/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.fail API documentation @@ -142,7 +142,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/get_thread_context/async_get_thread_context.html b/docs/reference/context/get_thread_context/async_get_thread_context.html index 66500752e..1c3fc4d6c 100644 --- a/docs/reference/context/get_thread_context/async_get_thread_context.html +++ b/docs/reference/context/get_thread_context/async_get_thread_context.html @@ -3,7 +3,7 @@ - + slack_bolt.context.get_thread_context.async_get_thread_context API documentation @@ -154,7 +154,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/get_thread_context/get_thread_context.html b/docs/reference/context/get_thread_context/get_thread_context.html index a6777da30..4ac274368 100644 --- a/docs/reference/context/get_thread_context/get_thread_context.html +++ b/docs/reference/context/get_thread_context/get_thread_context.html @@ -3,7 +3,7 @@ - + slack_bolt.context.get_thread_context.get_thread_context API documentation @@ -154,7 +154,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/get_thread_context/index.html b/docs/reference/context/get_thread_context/index.html index ffd095911..13dcd1388 100644 --- a/docs/reference/context/get_thread_context/index.html +++ b/docs/reference/context/get_thread_context/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.get_thread_context API documentation @@ -171,7 +171,7 @@

      diff --git a/docs/reference/context/index.html b/docs/reference/context/index.html index 4d3f472cc..65cb8054c 100644 --- a/docs/reference/context/index.html +++ b/docs/reference/context/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context API documentation @@ -790,7 +790,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/respond/async_respond.html b/docs/reference/context/respond/async_respond.html index 148e1607e..ed071afaf 100644 --- a/docs/reference/context/respond/async_respond.html +++ b/docs/reference/context/respond/async_respond.html @@ -3,7 +3,7 @@ - + slack_bolt.context.respond.async_respond API documentation @@ -160,7 +160,7 @@

      diff --git a/docs/reference/context/respond/index.html b/docs/reference/context/respond/index.html index 94693dccf..8c116c956 100644 --- a/docs/reference/context/respond/index.html +++ b/docs/reference/context/respond/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.respond API documentation @@ -182,7 +182,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/respond/internals.html b/docs/reference/context/respond/internals.html index 295a793a3..e61988ef6 100644 --- a/docs/reference/context/respond/internals.html +++ b/docs/reference/context/respond/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.context.respond.internals API documentation @@ -60,7 +60,7 @@

      Module slack_bolt.context.respond.internals

      diff --git a/docs/reference/context/respond/respond.html b/docs/reference/context/respond/respond.html index 5fb010d20..af2271eb6 100644 --- a/docs/reference/context/respond/respond.html +++ b/docs/reference/context/respond/respond.html @@ -3,7 +3,7 @@ - + slack_bolt.context.respond.respond API documentation @@ -160,7 +160,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/save_thread_context/async_save_thread_context.html b/docs/reference/context/save_thread_context/async_save_thread_context.html index 796970dd7..f57291c3c 100644 --- a/docs/reference/context/save_thread_context/async_save_thread_context.html +++ b/docs/reference/context/save_thread_context/async_save_thread_context.html @@ -3,7 +3,7 @@ - + slack_bolt.context.save_thread_context.async_save_thread_context API documentation @@ -123,7 +123,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/save_thread_context/index.html b/docs/reference/context/save_thread_context/index.html index 8b974a213..01f63ecd8 100644 --- a/docs/reference/context/save_thread_context/index.html +++ b/docs/reference/context/save_thread_context/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.save_thread_context API documentation @@ -140,7 +140,7 @@

      diff --git a/docs/reference/context/save_thread_context/save_thread_context.html b/docs/reference/context/save_thread_context/save_thread_context.html index 17c147505..328441034 100644 --- a/docs/reference/context/save_thread_context/save_thread_context.html +++ b/docs/reference/context/save_thread_context/save_thread_context.html @@ -3,7 +3,7 @@ - + slack_bolt.context.save_thread_context.save_thread_context API documentation @@ -123,7 +123,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/say/async_say.html b/docs/reference/context/say/async_say.html index 78d0b83a6..8547a1188 100644 --- a/docs/reference/context/say/async_say.html +++ b/docs/reference/context/say/async_say.html @@ -3,7 +3,7 @@ - + slack_bolt.context.say.async_say API documentation @@ -183,7 +183,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/say/index.html b/docs/reference/context/say/index.html index 5e2897f38..7a5850760 100644 --- a/docs/reference/context/say/index.html +++ b/docs/reference/context/say/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.say API documentation @@ -214,7 +214,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/say/internals.html b/docs/reference/context/say/internals.html index ac349a4b5..861065203 100644 --- a/docs/reference/context/say/internals.html +++ b/docs/reference/context/say/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.context.say.internals API documentation @@ -60,7 +60,7 @@

      Module slack_bolt.context.say.internals

      diff --git a/docs/reference/context/say/say.html b/docs/reference/context/say/say.html index 20ad41c0e..5db4f24ba 100644 --- a/docs/reference/context/say/say.html +++ b/docs/reference/context/say/say.html @@ -3,7 +3,7 @@ - + slack_bolt.context.say.say API documentation @@ -192,7 +192,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_status/async_set_status.html b/docs/reference/context/set_status/async_set_status.html index d22fb3aa8..6a15d70ae 100644 --- a/docs/reference/context/set_status/async_set_status.html +++ b/docs/reference/context/set_status/async_set_status.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_status.async_set_status API documentation @@ -123,7 +123,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_status/index.html b/docs/reference/context/set_status/index.html index 5a2e8be48..9e53da9a5 100644 --- a/docs/reference/context/set_status/index.html +++ b/docs/reference/context/set_status/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_status API documentation @@ -140,7 +140,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_status/set_status.html b/docs/reference/context/set_status/set_status.html index 337fbf576..0ec8df5da 100644 --- a/docs/reference/context/set_status/set_status.html +++ b/docs/reference/context/set_status/set_status.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_status.set_status API documentation @@ -123,7 +123,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html index ee7458fbc..449a72117 100644 --- a/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html +++ b/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts API documentation @@ -135,7 +135,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_suggested_prompts/index.html b/docs/reference/context/set_suggested_prompts/index.html index f3084288a..ee5371cea 100644 --- a/docs/reference/context/set_suggested_prompts/index.html +++ b/docs/reference/context/set_suggested_prompts/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_suggested_prompts API documentation @@ -152,7 +152,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html index bdb31a3ca..133d3a55a 100644 --- a/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html +++ b/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_suggested_prompts.set_suggested_prompts API documentation @@ -135,7 +135,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_title/async_set_title.html b/docs/reference/context/set_title/async_set_title.html index 9c195664c..e7db1ca1c 100644 --- a/docs/reference/context/set_title/async_set_title.html +++ b/docs/reference/context/set_title/async_set_title.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_title.async_set_title API documentation @@ -123,7 +123,7 @@

      diff --git a/docs/reference/context/set_title/index.html b/docs/reference/context/set_title/index.html index 4c88c8539..7ae070fe8 100644 --- a/docs/reference/context/set_title/index.html +++ b/docs/reference/context/set_title/index.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_title API documentation @@ -140,7 +140,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/context/set_title/set_title.html b/docs/reference/context/set_title/set_title.html index 59a4498bf..cd4d1e27e 100644 --- a/docs/reference/context/set_title/set_title.html +++ b/docs/reference/context/set_title/set_title.html @@ -3,7 +3,7 @@ - + slack_bolt.context.set_title.set_title API documentation @@ -123,7 +123,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/error/index.html b/docs/reference/error/index.html index 8e578eb61..f57d690e9 100644 --- a/docs/reference/error/index.html +++ b/docs/reference/error/index.html @@ -3,7 +3,7 @@ - + slack_bolt.error API documentation @@ -160,7 +160,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/index.html b/docs/reference/index.html index 6aae1d55a..430e36813 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -3,7 +3,7 @@ - + slack_bolt API documentation @@ -815,7 +815,7 @@

      Class variables

      """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -833,7 +833,7 @@

      Class variables

      # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -850,7 +850,7 @@

      Class variables

      warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -927,7 +927,7 @@

      Class variables

      # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -965,7 +965,7 @@

      Class variables

      # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1011,6 +1011,7 @@

      Class variables

      matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -1039,13 +1040,17 @@

      Class variables

      Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) + matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ @@ -1071,7 +1076,7 @@

      Class variables

      # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1118,7 +1123,7 @@

      Class variables

      # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1186,9 +1191,9 @@

      Class variables

      # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1214,7 +1219,7 @@

      Class variables

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1231,7 +1236,7 @@

      Class variables

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1247,7 +1252,7 @@

      Class variables

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1263,7 +1268,7 @@

      Class variables

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1304,7 +1309,7 @@

      Class variables

      # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1330,7 +1335,7 @@

      Class variables

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1346,7 +1351,7 @@

      Class variables

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1387,7 +1392,8 @@

      Class variables

      Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1427,7 +1433,7 @@

      Class variables

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1520,6 +1526,7 @@

      Class variables

      matchers: Optional[Sequence[Callable[..., bool]]], middleware: Optional[Sequence[Union[Callable, Middleware]]], auto_acknowledgement: bool = False, + ack_timeout: int = 3, ) -> Optional[Callable[..., Optional[BoltResponse]]]: value_to_return = None if not isinstance(functions, list): @@ -1546,10 +1553,11 @@

      Class variables

      CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, + lazy_functions=functions, # type:ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, + ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) @@ -1769,9 +1777,9 @@

      Methods

      # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for actions in `blocks`. - * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for actions in `attachments`. - * Refer to https://docs.slack.dev/legacy/legacy-dialogs for actions in dialogs. + * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. + * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. + * Refer to https://api.slack.com/dialogs for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1800,9 +1808,9 @@

      Methods

      app.action("approve_button")(update_message)

      To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

      Args

      @@ -1845,7 +1853,7 @@

      Args

      middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.""" + Refer to https://api.slack.com/legacy/message-buttons for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1855,7 +1863,7 @@

      Args

      return __call__

      Registers a new interactive_message action listener. -Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons for details.

      +Refer to https://api.slack.com/legacy/message-buttons for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1872,7 +1880,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details. + Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. """ def __call__(*args, **kwargs): @@ -1883,7 +1891,7 @@

    Args

    return __call__

    Registers a new block_actions action listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1937,7 +1945,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands. + Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1968,7 +1976,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details of Slash Commands.

    +

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2031,7 +2039,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2041,7 +2049,7 @@

    Args

    return __call__

    Registers a new dialog_cancellation listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -2058,7 +2066,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2068,7 +2076,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -2085,7 +2093,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.""" + Refer to https://api.slack.com/dialogs for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2095,7 +2103,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://docs.slack.dev/legacy/legacy-dialogs for details.

    +Refer to https://api.slack.com/dialogs for details.

    def dispatch(self,
    req: BoltRequest) ‑> BoltResponse
    @@ -2321,7 +2329,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. + Refer to https://api.slack.com/apis/connections/events-api for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2353,7 +2361,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    +

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2369,7 +2377,7 @@

    Args

    -def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None,
    auto_acknowledge: bool = True) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    +def function(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None,
    auto_acknowledge: bool = True,
    ack_timeout: int = 3) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -2382,6 +2390,7 @@

    Args

    matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, + ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. @@ -2410,13 +2419,17 @@

    Args

    Only when all the middleware call `next()` method, the listener function can be invoked. """ + if auto_acknowledge is True: + if ack_timeout != 3: + self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) + matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__
    @@ -2500,7 +2513,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://docs.slack.dev/reference/events/message for details of `message` events. + Refer to https://api.slack.com/events/message for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2551,7 +2564,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://docs.slack.dev/reference/events/message for details of message events.

    +

    Refer to https://api.slack.com/events/message for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2694,7 +2707,8 @@

    Args

    Refer to the following documents for details: - * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select + * https://api.slack.com/reference/block-kit/block-elements#external_select + * https://api.slack.com/reference/block-kit/block-elements#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2734,7 +2748,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2780,7 +2795,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts. + Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2817,7 +2832,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts for details about Shortcuts.

    +

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2903,7 +2918,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new step from app listener. @@ -2921,7 +2936,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. + Refer to https://api.slack.com/workflows/steps for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2938,7 +2953,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps" + "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" ), category=DeprecationWarning, ) @@ -2960,7 +2975,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use WorkflowStepBuilder's methods.

    @@ -2975,7 +2990,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    +

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    For further information about WorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -3046,7 +3061,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. + Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -3087,7 +3102,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    +

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -3116,7 +3131,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3126,7 +3141,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_closed for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -3143,7 +3158,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.""" + Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3153,7 +3168,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload#view_submission for details.

    +Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    @@ -5307,6 +5322,7 @@

    Methods

    ack_function: Callable[..., BoltResponse] lazy_functions: Sequence[Callable[..., None]] auto_acknowledgement: bool + ack_timeout: int = 3 def matches( self, @@ -5372,6 +5388,10 @@

    Class variables

    The type of the None singleton.

    +
    var ack_timeout : int
    +
    +

    The type of the None singleton.

    +
    var auto_acknowledgement : bool

    The type of the None singleton.

    @@ -6115,6 +6135,7 @@

    Listener

    • ack_function
    • +
    • ack_timeout
    • auto_acknowledgement
    • lazy_functions
    • matchers
    • @@ -6180,7 +6201,7 @@

      SetTitle diff --git a/docs/reference/kwargs_injection/args.html b/docs/reference/kwargs_injection/args.html index 9d2eb2c40..4d03687d1 100644 --- a/docs/reference/kwargs_injection/args.html +++ b/docs/reference/kwargs_injection/args.html @@ -3,7 +3,7 @@ - + slack_bolt.kwargs_injection.args API documentation @@ -404,7 +404,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/kwargs_injection/async_args.html b/docs/reference/kwargs_injection/async_args.html index 65a14c2d1..959f35a43 100644 --- a/docs/reference/kwargs_injection/async_args.html +++ b/docs/reference/kwargs_injection/async_args.html @@ -3,7 +3,7 @@ - + slack_bolt.kwargs_injection.async_args API documentation @@ -401,7 +401,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/kwargs_injection/async_utils.html b/docs/reference/kwargs_injection/async_utils.html index 6f433ddca..80952518d 100644 --- a/docs/reference/kwargs_injection/async_utils.html +++ b/docs/reference/kwargs_injection/async_utils.html @@ -3,7 +3,7 @@ - + slack_bolt.kwargs_injection.async_utils API documentation @@ -171,7 +171,7 @@

      Functions

      diff --git a/docs/reference/kwargs_injection/index.html b/docs/reference/kwargs_injection/index.html index 4132bbaba..de7ef4a0a 100644 --- a/docs/reference/kwargs_injection/index.html +++ b/docs/reference/kwargs_injection/index.html @@ -3,7 +3,7 @@ - + slack_bolt.kwargs_injection API documentation @@ -544,7 +544,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/kwargs_injection/utils.html b/docs/reference/kwargs_injection/utils.html index a589350c9..2e6ecd001 100644 --- a/docs/reference/kwargs_injection/utils.html +++ b/docs/reference/kwargs_injection/utils.html @@ -3,7 +3,7 @@ - + slack_bolt.kwargs_injection.utils API documentation @@ -170,7 +170,7 @@

      Functions

      diff --git a/docs/reference/lazy_listener/async_internals.html b/docs/reference/lazy_listener/async_internals.html index 19becac19..9d86a02e5 100644 --- a/docs/reference/lazy_listener/async_internals.html +++ b/docs/reference/lazy_listener/async_internals.html @@ -3,7 +3,7 @@ - + slack_bolt.lazy_listener.async_internals API documentation @@ -102,7 +102,7 @@

      Functions

      diff --git a/docs/reference/lazy_listener/async_runner.html b/docs/reference/lazy_listener/async_runner.html index e58b0a044..701f1640a 100644 --- a/docs/reference/lazy_listener/async_runner.html +++ b/docs/reference/lazy_listener/async_runner.html @@ -3,7 +3,7 @@ - + slack_bolt.lazy_listener.async_runner API documentation @@ -184,7 +184,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/lazy_listener/asyncio_runner.html b/docs/reference/lazy_listener/asyncio_runner.html index d05a4c9ac..2fdcf8ffe 100644 --- a/docs/reference/lazy_listener/asyncio_runner.html +++ b/docs/reference/lazy_listener/asyncio_runner.html @@ -3,7 +3,7 @@ - + slack_bolt.lazy_listener.asyncio_runner API documentation @@ -113,7 +113,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/lazy_listener/index.html b/docs/reference/lazy_listener/index.html index 374164af8..c2eb1c9b0 100644 --- a/docs/reference/lazy_listener/index.html +++ b/docs/reference/lazy_listener/index.html @@ -3,7 +3,7 @@ - + slack_bolt.lazy_listener API documentation @@ -295,7 +295,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/lazy_listener/internals.html b/docs/reference/lazy_listener/internals.html index 96c04a56c..1801abafd 100644 --- a/docs/reference/lazy_listener/internals.html +++ b/docs/reference/lazy_listener/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.lazy_listener.internals API documentation @@ -102,7 +102,7 @@

      Functions

      diff --git a/docs/reference/lazy_listener/runner.html b/docs/reference/lazy_listener/runner.html index 56216c9c8..ff4f449a0 100644 --- a/docs/reference/lazy_listener/runner.html +++ b/docs/reference/lazy_listener/runner.html @@ -3,7 +3,7 @@ - + slack_bolt.lazy_listener.runner API documentation @@ -185,7 +185,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/lazy_listener/thread_runner.html b/docs/reference/lazy_listener/thread_runner.html index 19e6ff29e..b4ca0711a 100644 --- a/docs/reference/lazy_listener/thread_runner.html +++ b/docs/reference/lazy_listener/thread_runner.html @@ -3,7 +3,7 @@ - + slack_bolt.lazy_listener.thread_runner API documentation @@ -119,7 +119,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/listener/async_builtins.html b/docs/reference/listener/async_builtins.html index 61e6fcd9f..015dd94b3 100644 --- a/docs/reference/listener/async_builtins.html +++ b/docs/reference/listener/async_builtins.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.async_builtins API documentation @@ -168,7 +168,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/listener/async_listener.html b/docs/reference/listener/async_listener.html index 52da6d342..a3d1a7fef 100644 --- a/docs/reference/listener/async_listener.html +++ b/docs/reference/listener/async_listener.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.async_listener API documentation @@ -48,7 +48,7 @@

      Classes

      class AsyncCustomListener -(*,
      app_name: str,
      ack_function: Callable[..., Awaitable[BoltResponse | None]],
      lazy_functions: Sequence[Callable[..., Awaitable[None]]],
      matchers: Sequence[AsyncListenerMatcher],
      middleware: Sequence[AsyncMiddleware],
      auto_acknowledgement: bool = False,
      base_logger: logging.Logger | None = None)
      +(*,
      app_name: str,
      ack_function: Callable[..., Awaitable[BoltResponse | None]],
      lazy_functions: Sequence[Callable[..., Awaitable[None]]],
      matchers: Sequence[AsyncListenerMatcher],
      middleware: Sequence[AsyncMiddleware],
      auto_acknowledgement: bool = False,
      ack_timeout: int = 3,
      base_logger: logging.Logger | None = None)
      @@ -62,6 +62,7 @@

      Classes

      matchers: Sequence[AsyncListenerMatcher] middleware: Sequence[AsyncMiddleware] auto_acknowledgement: bool + ack_timeout: int arg_names: MutableSequence[str] logger: Logger @@ -74,6 +75,7 @@

      Classes

      matchers: Sequence[AsyncListenerMatcher], middleware: Sequence[AsyncMiddleware], auto_acknowledgement: bool = False, + ack_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -82,6 +84,7 @@

      Classes

      self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement + self.ack_timeout = ack_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) @@ -112,6 +115,10 @@

      Class variables

      The type of the None singleton.

      +
      var ack_timeout : int
      +
      +

      The type of the None singleton.

      +
      var app_name : str

      The type of the None singleton.

      @@ -182,7 +189,7 @@

      Returns

      class cls -(*,
      app_name: str,
      ack_function: Callable[..., Awaitable[BoltResponse | None]],
      lazy_functions: Sequence[Callable[..., Awaitable[None]]],
      matchers: Sequence[AsyncListenerMatcher],
      middleware: Sequence[AsyncMiddleware],
      auto_acknowledgement: bool = False,
      base_logger: logging.Logger | None = None)
      +(*,
      app_name: str,
      ack_function: Callable[..., Awaitable[BoltResponse | None]],
      lazy_functions: Sequence[Callable[..., Awaitable[None]]],
      matchers: Sequence[AsyncListenerMatcher],
      middleware: Sequence[AsyncMiddleware],
      auto_acknowledgement: bool = False,
      ack_timeout: int = 3,
      base_logger: logging.Logger | None = None)
      @@ -196,6 +203,7 @@

      Returns

      matchers: Sequence[AsyncListenerMatcher] middleware: Sequence[AsyncMiddleware] auto_acknowledgement: bool + ack_timeout: int arg_names: MutableSequence[str] logger: Logger @@ -208,6 +216,7 @@

      Returns

      matchers: Sequence[AsyncListenerMatcher], middleware: Sequence[AsyncMiddleware], auto_acknowledgement: bool = False, + ack_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -216,6 +225,7 @@

      Returns

      self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement + self.ack_timeout = ack_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) @@ -260,6 +270,7 @@

      Inherited members

    • AsyncListener:
      • ack_function
      • +
      • ack_timeout
      • auto_acknowledgement
      • lazy_functions
      • matchers
      • @@ -284,6 +295,7 @@

        Inherited members

        ack_function: Callable[..., Awaitable[BoltResponse]] lazy_functions: Sequence[Callable[..., Awaitable[None]]] auto_acknowledgement: bool + ack_timeout: int async def async_matches( self, @@ -349,6 +361,10 @@

        Class variables

        The type of the None singleton.

        +
        var ack_timeout : int
        +
        +

        The type of the None singleton.

        +
        var auto_acknowledgement : bool

        The type of the None singleton.

        @@ -490,6 +506,7 @@

        Returns

        AsyncCustomListener

        • ack_function
        • +
        • ack_timeout
        • app_name
        • arg_names
        • auto_acknowledgement
        • @@ -512,6 +529,7 @@

          AsyncListener

          • ack_function
          • +
          • ack_timeout
          • async_matches
          • auto_acknowledgement
          • lazy_functions
          • @@ -527,7 +545,7 @@

            -

            Generated by pdoc 0.11.5.

            +

            Generated by pdoc 0.11.6.

            diff --git a/docs/reference/listener/async_listener_completion_handler.html b/docs/reference/listener/async_listener_completion_handler.html index 2a05e0213..6cde66b93 100644 --- a/docs/reference/listener/async_listener_completion_handler.html +++ b/docs/reference/listener/async_listener_completion_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.async_listener_completion_handler API documentation @@ -220,7 +220,7 @@

            -

            Generated by pdoc 0.11.5.

            +

            Generated by pdoc 0.11.6.

            diff --git a/docs/reference/listener/async_listener_error_handler.html b/docs/reference/listener/async_listener_error_handler.html index 9600a2cfd..1f3789c40 100644 --- a/docs/reference/listener/async_listener_error_handler.html +++ b/docs/reference/listener/async_listener_error_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.async_listener_error_handler API documentation @@ -234,7 +234,7 @@

            -

            Generated by pdoc 0.11.5.

            +

            Generated by pdoc 0.11.6.

            diff --git a/docs/reference/listener/async_listener_start_handler.html b/docs/reference/listener/async_listener_start_handler.html index 23ada5e08..80b25eb29 100644 --- a/docs/reference/listener/async_listener_start_handler.html +++ b/docs/reference/listener/async_listener_start_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.async_listener_start_handler API documentation @@ -220,7 +220,7 @@

            -

            Generated by pdoc 0.11.5.

            +

            Generated by pdoc 0.11.6.

            diff --git a/docs/reference/listener/asyncio_runner.html b/docs/reference/listener/asyncio_runner.html index 8262667f0..4d71a88a7 100644 --- a/docs/reference/listener/asyncio_runner.html +++ b/docs/reference/listener/asyncio_runner.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.asyncio_runner API documentation @@ -180,7 +180,7 @@

            Classes

            self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= 3: + while ack.response is None and time.time() - starting_time <= listener.ack_timeout: await asyncio.sleep(0.01) if response is None and ack.response is None: @@ -359,7 +359,7 @@

            Methods

            self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= 3: + while ack.response is None and time.time() - starting_time <= listener.ack_timeout: await asyncio.sleep(0.01) if response is None and ack.response is None: @@ -414,7 +414,7 @@

            diff --git a/docs/reference/listener/builtins.html b/docs/reference/listener/builtins.html index 75f8ca620..5f3759658 100644 --- a/docs/reference/listener/builtins.html +++ b/docs/reference/listener/builtins.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.builtins API documentation @@ -168,7 +168,7 @@

            diff --git a/docs/reference/listener/custom_listener.html b/docs/reference/listener/custom_listener.html index 1cd261379..1f18502f2 100644 --- a/docs/reference/listener/custom_listener.html +++ b/docs/reference/listener/custom_listener.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.custom_listener API documentation @@ -48,7 +48,7 @@

            Classes

            class CustomListener -(*,
            app_name: str,
            ack_function: Callable[..., BoltResponse | None],
            lazy_functions: Sequence[Callable[..., None]],
            matchers: Sequence[ListenerMatcher],
            middleware: Sequence[Middleware],
            auto_acknowledgement: bool = False,
            base_logger: logging.Logger | None = None)
            +(*,
            app_name: str,
            ack_function: Callable[..., BoltResponse | None],
            lazy_functions: Sequence[Callable[..., None]],
            matchers: Sequence[ListenerMatcher],
            middleware: Sequence[Middleware],
            auto_acknowledgement: bool = False,
            ack_timeout: int = 3,
            base_logger: logging.Logger | None = None)
            @@ -62,6 +62,7 @@

            Classes

            matchers: Sequence[ListenerMatcher] middleware: Sequence[Middleware] auto_acknowledgement: bool + ack_timeout: int = 3 arg_names: MutableSequence[str] logger: Logger @@ -74,6 +75,7 @@

            Classes

            matchers: Sequence[ListenerMatcher], middleware: Sequence[Middleware], auto_acknowledgement: bool = False, + ack_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -82,6 +84,7 @@

            Classes

            self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement + self.ack_timeout = ack_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) @@ -126,6 +129,7 @@

            Inherited members

          • Listener:
            • ack_function
            • +
            • ack_timeout
            • auto_acknowledgement
            • lazy_functions
            • matchers
            • @@ -165,7 +169,7 @@

              -

              Generated by pdoc 0.11.5.

              +

              Generated by pdoc 0.11.6.

              diff --git a/docs/reference/listener/index.html b/docs/reference/listener/index.html index 677147e21..f31264cac 100644 --- a/docs/reference/listener/index.html +++ b/docs/reference/listener/index.html @@ -3,7 +3,7 @@ - + slack_bolt.listener API documentation @@ -107,7 +107,7 @@

              Classes

              class CustomListener -(*,
              app_name: str,
              ack_function: Callable[..., BoltResponse | None],
              lazy_functions: Sequence[Callable[..., None]],
              matchers: Sequence[ListenerMatcher],
              middleware: Sequence[Middleware],
              auto_acknowledgement: bool = False,
              base_logger: logging.Logger | None = None)
              +(*,
              app_name: str,
              ack_function: Callable[..., BoltResponse | None],
              lazy_functions: Sequence[Callable[..., None]],
              matchers: Sequence[ListenerMatcher],
              middleware: Sequence[Middleware],
              auto_acknowledgement: bool = False,
              ack_timeout: int = 3,
              base_logger: logging.Logger | None = None)
              @@ -121,6 +121,7 @@

              Classes

              matchers: Sequence[ListenerMatcher] middleware: Sequence[Middleware] auto_acknowledgement: bool + ack_timeout: int = 3 arg_names: MutableSequence[str] logger: Logger @@ -133,6 +134,7 @@

              Classes

              matchers: Sequence[ListenerMatcher], middleware: Sequence[Middleware], auto_acknowledgement: bool = False, + ack_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -141,6 +143,7 @@

              Classes

              self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement + self.ack_timeout = ack_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) @@ -185,6 +188,7 @@

              Inherited members

            • Listener:
              • ack_function
              • +
              • ack_timeout
              • auto_acknowledgement
              • lazy_functions
              • matchers
              • @@ -209,6 +213,7 @@

                Inherited members

                ack_function: Callable[..., BoltResponse] lazy_functions: Sequence[Callable[..., None]] auto_acknowledgement: bool + ack_timeout: int = 3 def matches( self, @@ -274,6 +279,10 @@

                Class variables

                The type of the None singleton.

                +
                var ack_timeout : int
                +
                +

                The type of the None singleton.

                +
                var auto_acknowledgement : bool

                The type of the None singleton.

                @@ -440,6 +449,7 @@

                Listener

                • ack_function
                • +
                • ack_timeout
                • auto_acknowledgement
                • lazy_functions
                • matchers
                • @@ -455,7 +465,7 @@

                  -

                  Generated by pdoc 0.11.5.

                  +

                  Generated by pdoc 0.11.6.

                  diff --git a/docs/reference/listener/listener.html b/docs/reference/listener/listener.html index 743fb2ceb..034dbe67f 100644 --- a/docs/reference/listener/listener.html +++ b/docs/reference/listener/listener.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.listener API documentation @@ -60,6 +60,7 @@

                  Classes

                  ack_function: Callable[..., BoltResponse] lazy_functions: Sequence[Callable[..., None]] auto_acknowledgement: bool + ack_timeout: int = 3 def matches( self, @@ -125,6 +126,10 @@

                  Class variables

                  The type of the None singleton.

                  +
                  var ack_timeout : int
                  +
                  +

                  The type of the None singleton.

                  +
                  var auto_acknowledgement : bool

                  The type of the None singleton.

                  @@ -266,6 +271,7 @@

                  Returns

                  Listener

                  • ack_function
                  • +
                  • ack_timeout
                  • auto_acknowledgement
                  • lazy_functions
                  • matchers
                  • @@ -281,7 +287,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener/listener_completion_handler.html b/docs/reference/listener/listener_completion_handler.html index 35b2fe8cd..42b1b5413 100644 --- a/docs/reference/listener/listener_completion_handler.html +++ b/docs/reference/listener/listener_completion_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.listener_completion_handler API documentation @@ -221,7 +221,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener/listener_error_handler.html b/docs/reference/listener/listener_error_handler.html index fc49894d2..c9f7c2ccd 100644 --- a/docs/reference/listener/listener_error_handler.html +++ b/docs/reference/listener/listener_error_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.listener_error_handler API documentation @@ -234,7 +234,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener/listener_start_handler.html b/docs/reference/listener/listener_start_handler.html index 63cb98b91..d60c1b9dc 100644 --- a/docs/reference/listener/listener_start_handler.html +++ b/docs/reference/listener/listener_start_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.listener_start_handler API documentation @@ -232,7 +232,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener/thread_runner.html b/docs/reference/listener/thread_runner.html index b6fafae99..5415f9ada 100644 --- a/docs/reference/listener/thread_runner.html +++ b/docs/reference/listener/thread_runner.html @@ -3,7 +3,7 @@ - + slack_bolt.listener.thread_runner API documentation @@ -148,7 +148,7 @@

                    Classes

                    if not request.lazy_only: # start the listener function asynchronously def run_ack_function_asynchronously(): - nonlocal ack, request, response + nonlocal response try: self.listener_start_handler.handle( request=request, @@ -197,7 +197,7 @@

                    Classes

                    self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= 3: + while ack.response is None and time.time() - starting_time <= listener.ack_timeout: time.sleep(0.01) if response is None and ack.response is None: @@ -346,7 +346,7 @@

                    Methods

                    if not request.lazy_only: # start the listener function asynchronously def run_ack_function_asynchronously(): - nonlocal ack, request, response + nonlocal response try: self.listener_start_handler.handle( request=request, @@ -395,7 +395,7 @@

                    Methods

                    self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= 3: + while ack.response is None and time.time() - starting_time <= listener.ack_timeout: time.sleep(0.01) if response is None and ack.response is None: @@ -451,7 +451,7 @@

                    diff --git a/docs/reference/listener_matcher/async_builtins.html b/docs/reference/listener_matcher/async_builtins.html index b99d07c82..0df1215de 100644 --- a/docs/reference/listener_matcher/async_builtins.html +++ b/docs/reference/listener_matcher/async_builtins.html @@ -3,7 +3,7 @@ - + slack_bolt.listener_matcher.async_builtins API documentation @@ -112,7 +112,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener_matcher/async_listener_matcher.html b/docs/reference/listener_matcher/async_listener_matcher.html index bc8676302..1366da4e2 100644 --- a/docs/reference/listener_matcher/async_listener_matcher.html +++ b/docs/reference/listener_matcher/async_listener_matcher.html @@ -3,7 +3,7 @@ - + slack_bolt.listener_matcher.async_listener_matcher API documentation @@ -311,7 +311,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener_matcher/builtins.html b/docs/reference/listener_matcher/builtins.html index 29af67f6c..d951deada 100644 --- a/docs/reference/listener_matcher/builtins.html +++ b/docs/reference/listener_matcher/builtins.html @@ -3,7 +3,7 @@ - + slack_bolt.listener_matcher.builtins API documentation @@ -80,7 +80,7 @@

                    Functions

                    return dialog_submission(constraints["callback_id"], asyncio) if action_type == "dialog_cancellation": return dialog_cancellation(constraints["callback_id"], asyncio) - # https://docs.slack.dev/legacy/legacy-steps-from-apps/ + # https://api.slack.com/workflows/steps if action_type == "workflow_step_edit": return workflow_step_edit(constraints["callback_id"], asyncio) @@ -692,7 +692,7 @@

                    diff --git a/docs/reference/listener_matcher/custom_listener_matcher.html b/docs/reference/listener_matcher/custom_listener_matcher.html index 8009e84a1..087d36907 100644 --- a/docs/reference/listener_matcher/custom_listener_matcher.html +++ b/docs/reference/listener_matcher/custom_listener_matcher.html @@ -3,7 +3,7 @@ - + slack_bolt.listener_matcher.custom_listener_matcher API documentation @@ -141,7 +141,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener_matcher/index.html b/docs/reference/listener_matcher/index.html index 622a5e9d9..a93c86d98 100644 --- a/docs/reference/listener_matcher/index.html +++ b/docs/reference/listener_matcher/index.html @@ -3,7 +3,7 @@ - + slack_bolt.listener_matcher API documentation @@ -247,7 +247,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/listener_matcher/listener_matcher.html b/docs/reference/listener_matcher/listener_matcher.html index a20816088..0618f7e4e 100644 --- a/docs/reference/listener_matcher/listener_matcher.html +++ b/docs/reference/listener_matcher/listener_matcher.html @@ -3,7 +3,7 @@ - + slack_bolt.listener_matcher.listener_matcher API documentation @@ -137,7 +137,7 @@

                    -

                    Generated by pdoc 0.11.5.

                    +

                    Generated by pdoc 0.11.6.

                    diff --git a/docs/reference/logger/index.html b/docs/reference/logger/index.html index c9defacdb..d0b2ef33f 100644 --- a/docs/reference/logger/index.html +++ b/docs/reference/logger/index.html @@ -3,7 +3,7 @@ - + slack_bolt.logger API documentation @@ -121,7 +121,7 @@

                    Functions

                    diff --git a/docs/reference/logger/messages.html b/docs/reference/logger/messages.html index c3ff45156..3c8d67a31 100644 --- a/docs/reference/logger/messages.html +++ b/docs/reference/logger/messages.html @@ -3,7 +3,7 @@ - + slack_bolt.logger.messages API documentation @@ -308,6 +308,20 @@

                    Functions

            • +
              +def warning_ack_timeout_has_no_effect(identifier: str | re.Pattern, ack_timeout: int) ‑> str +
              +
              +
              + +Expand source code + +
              def warning_ack_timeout_has_no_effect(identifier: Union[str, Pattern], ack_timeout: int) -> str:
              +    handler_example = f'@app.function("{identifier}")' if isinstance(identifier, str) else f"@app.function({identifier})"
              +    return f"On {handler_example}, as `auto_acknowledge` is `True`, " f"`ack_timeout={ack_timeout}` you gave will be unused"
              +
              +
              +
              def warning_bot_only_conflicts() ‑> str
              @@ -591,6 +605,7 @@

              Functions

            • error_token_required
            • error_unexpected_listener_middleware
            • info_default_oauth_settings_loaded
            • +
            • warning_ack_timeout_has_no_effect
            • warning_bot_only_conflicts
            • warning_client_prioritized_and_token_skipped
            • warning_did_not_call_ack
            • @@ -605,7 +620,7 @@

              Functions

              diff --git a/docs/reference/middleware/assistant/assistant.html b/docs/reference/middleware/assistant/assistant.html index 76e5dff76..d1184c407 100644 --- a/docs/reference/middleware/assistant/assistant.html +++ b/docs/reference/middleware/assistant/assistant.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.assistant.assistant API documentation @@ -647,7 +647,7 @@

              -

              Generated by pdoc 0.11.5.

              +

              Generated by pdoc 0.11.6.

              diff --git a/docs/reference/middleware/assistant/async_assistant.html b/docs/reference/middleware/assistant/async_assistant.html index 260d493ac..2faf0e34b 100644 --- a/docs/reference/middleware/assistant/async_assistant.html +++ b/docs/reference/middleware/assistant/async_assistant.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.assistant.async_assistant API documentation @@ -707,7 +707,7 @@

              -

              Generated by pdoc 0.11.5.

              +

              Generated by pdoc 0.11.6.

              diff --git a/docs/reference/middleware/assistant/index.html b/docs/reference/middleware/assistant/index.html index 857240adb..92f405cad 100644 --- a/docs/reference/middleware/assistant/index.html +++ b/docs/reference/middleware/assistant/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.assistant API documentation @@ -664,7 +664,7 @@

              -

              Generated by pdoc 0.11.5.

              +

              Generated by pdoc 0.11.6.

              diff --git a/docs/reference/middleware/async_builtins.html b/docs/reference/middleware/async_builtins.html index eb46581bb..7528dc0bb 100644 --- a/docs/reference/middleware/async_builtins.html +++ b/docs/reference/middleware/async_builtins.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.async_builtins API documentation @@ -205,7 +205,7 @@

              Inherited members

              """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details. + Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. """ async def async_process( @@ -232,10 +232,10 @@

              Inherited members

          • Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

            -

            Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details.

            +

            Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

            Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

            -

            Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details.

            +

            Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

            Args

            signing_secret
            @@ -293,12 +293,12 @@

            Inherited members

    • A middleware can process request data before other middleware and listener functions.

      Handles ssl_check requests. -Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details.

      +Refer to https://api.slack.com/interactivity/slash-commands for details.

      Args

      verification_token
      The verification token to check -(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation)
      +(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
      base_logger
      The base logger
      @@ -352,7 +352,7 @@

      Inherited members

      A middleware can process request data before other middleware and listener functions.

      Handles url_verification requests.

      -

      Refer to https://docs.slack.dev/reference/events/url_verification for details.

      +

      Refer to https://api.slack.com/events/url_verification for details.

      Args

      base_logger
      @@ -418,7 +418,7 @@

      diff --git a/docs/reference/middleware/async_custom_middleware.html b/docs/reference/middleware/async_custom_middleware.html index 6ef00baaf..d985458ed 100644 --- a/docs/reference/middleware/async_custom_middleware.html +++ b/docs/reference/middleware/async_custom_middleware.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.async_custom_middleware API documentation @@ -166,7 +166,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/async_middleware.html b/docs/reference/middleware/async_middleware.html index e0e38cf9c..33b4273e7 100644 --- a/docs/reference/middleware/async_middleware.html +++ b/docs/reference/middleware/async_middleware.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.async_middleware API documentation @@ -232,7 +232,7 @@

      diff --git a/docs/reference/middleware/async_middleware_error_handler.html b/docs/reference/middleware/async_middleware_error_handler.html index 617e490c7..e7cd8bb32 100644 --- a/docs/reference/middleware/async_middleware_error_handler.html +++ b/docs/reference/middleware/async_middleware_error_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.async_middleware_error_handler API documentation @@ -234,7 +234,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/attaching_function_token/async_attaching_function_token.html b/docs/reference/middleware/attaching_function_token/async_attaching_function_token.html index ab8b609a2..1becac04e 100644 --- a/docs/reference/middleware/attaching_function_token/async_attaching_function_token.html +++ b/docs/reference/middleware/attaching_function_token/async_attaching_function_token.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.attaching_function_token.async_attaching_function_token API documentation @@ -107,7 +107,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/attaching_function_token/attaching_function_token.html b/docs/reference/middleware/attaching_function_token/attaching_function_token.html index f005e8ac1..8eea36647 100644 --- a/docs/reference/middleware/attaching_function_token/attaching_function_token.html +++ b/docs/reference/middleware/attaching_function_token/attaching_function_token.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.attaching_function_token.attaching_function_token API documentation @@ -107,7 +107,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/attaching_function_token/index.html b/docs/reference/middleware/attaching_function_token/index.html index 107ae09d6..44efd27a2 100644 --- a/docs/reference/middleware/attaching_function_token/index.html +++ b/docs/reference/middleware/attaching_function_token/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.attaching_function_token API documentation @@ -124,7 +124,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/authorization/async_authorization.html b/docs/reference/middleware/authorization/async_authorization.html index aef0b3cd7..9f38ea711 100644 --- a/docs/reference/middleware/authorization/async_authorization.html +++ b/docs/reference/middleware/authorization/async_authorization.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.async_authorization API documentation @@ -102,7 +102,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/authorization/async_internals.html b/docs/reference/middleware/authorization/async_internals.html index 483e8c81f..22b709799 100644 --- a/docs/reference/middleware/authorization/async_internals.html +++ b/docs/reference/middleware/authorization/async_internals.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.async_internals API documentation @@ -60,7 +60,7 @@

      Module slack_bolt.middleware.authorization.async_interna diff --git a/docs/reference/middleware/authorization/async_multi_teams_authorization.html b/docs/reference/middleware/authorization/async_multi_teams_authorization.html index b9bfa138f..50b529f33 100644 --- a/docs/reference/middleware/authorization/async_multi_teams_authorization.html +++ b/docs/reference/middleware/authorization/async_multi_teams_authorization.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.async_multi_teams_authorization API documentation @@ -216,7 +216,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/authorization/async_single_team_authorization.html b/docs/reference/middleware/authorization/async_single_team_authorization.html index 2b3c40369..a167d1c68 100644 --- a/docs/reference/middleware/authorization/async_single_team_authorization.html +++ b/docs/reference/middleware/authorization/async_single_team_authorization.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.async_single_team_authorization API documentation @@ -157,7 +157,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/authorization/authorization.html b/docs/reference/middleware/authorization/authorization.html index 6922ca354..7ddd4ce41 100644 --- a/docs/reference/middleware/authorization/authorization.html +++ b/docs/reference/middleware/authorization/authorization.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.authorization API documentation @@ -101,7 +101,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/authorization/index.html b/docs/reference/middleware/authorization/index.html index f97cc17bb..9f5c3f393 100644 --- a/docs/reference/middleware/authorization/index.html +++ b/docs/reference/middleware/authorization/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization API documentation @@ -398,7 +398,7 @@

      diff --git a/docs/reference/middleware/authorization/internals.html b/docs/reference/middleware/authorization/internals.html index 62db9d31e..c64a7e0f3 100644 --- a/docs/reference/middleware/authorization/internals.html +++ b/docs/reference/middleware/authorization/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.internals API documentation @@ -60,7 +60,7 @@

      Module slack_bolt.middleware.authorization.internals diff --git a/docs/reference/middleware/authorization/multi_teams_authorization.html b/docs/reference/middleware/authorization/multi_teams_authorization.html index 1414820c6..c2a6a7964 100644 --- a/docs/reference/middleware/authorization/multi_teams_authorization.html +++ b/docs/reference/middleware/authorization/multi_teams_authorization.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.multi_teams_authorization API documentation @@ -213,7 +213,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/authorization/single_team_authorization.html b/docs/reference/middleware/authorization/single_team_authorization.html index 535b18532..7687be155 100644 --- a/docs/reference/middleware/authorization/single_team_authorization.html +++ b/docs/reference/middleware/authorization/single_team_authorization.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.authorization.single_team_authorization API documentation @@ -171,7 +171,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/custom_middleware.html b/docs/reference/middleware/custom_middleware.html index 4364973a6..aba9dc14b 100644 --- a/docs/reference/middleware/custom_middleware.html +++ b/docs/reference/middleware/custom_middleware.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.custom_middleware API documentation @@ -156,7 +156,7 @@

      diff --git a/docs/reference/middleware/ignoring_self_events/async_ignoring_self_events.html b/docs/reference/middleware/ignoring_self_events/async_ignoring_self_events.html index 5b0f31653..4d48b16b9 100644 --- a/docs/reference/middleware/ignoring_self_events/async_ignoring_self_events.html +++ b/docs/reference/middleware/ignoring_self_events/async_ignoring_self_events.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events API documentation @@ -125,7 +125,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/ignoring_self_events/ignoring_self_events.html b/docs/reference/middleware/ignoring_self_events/ignoring_self_events.html index 2c1a5eff3..111c096c4 100644 --- a/docs/reference/middleware/ignoring_self_events/ignoring_self_events.html +++ b/docs/reference/middleware/ignoring_self_events/ignoring_self_events.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.ignoring_self_events.ignoring_self_events API documentation @@ -170,7 +170,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/ignoring_self_events/index.html b/docs/reference/middleware/ignoring_self_events/index.html index 9c37fbb89..f81603f4a 100644 --- a/docs/reference/middleware/ignoring_self_events/index.html +++ b/docs/reference/middleware/ignoring_self_events/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.ignoring_self_events API documentation @@ -187,7 +187,7 @@

      -

      Generated by pdoc 0.11.5.

      +

      Generated by pdoc 0.11.6.

      diff --git a/docs/reference/middleware/index.html b/docs/reference/middleware/index.html index 7c8daecd1..98aa15c5d 100644 --- a/docs/reference/middleware/index.html +++ b/docs/reference/middleware/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware API documentation @@ -639,7 +639,7 @@

      Inherited members

      """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details. + Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. Args: signing_secret: The signing secret @@ -688,7 +688,7 @@

      Inherited members

      A middleware can process request data before other middleware and listener functions.

      Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

      -

      Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details.

      +

      Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

      Args

      signing_secret
      @@ -834,11 +834,11 @@

      Inherited members

      base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details. + Refer to https://api.slack.com/interactivity/slash-commands for details. Args: verification_token: The verification token to check - (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation) + (optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token @@ -880,12 +880,12 @@

      Inherited members

      A middleware can process request data before other middleware and listener functions.

      Handles slack_bolt.middleware.ssl_check requests. -Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details.

      +Refer to https://api.slack.com/interactivity/slash-commands for details.

      Args

      verification_token
      The verification token to check -(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation)
      +(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)

    base_logger
    The base logger
    @@ -931,7 +931,7 @@

    Inherited members

    def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. - Refer to https://docs.slack.dev/reference/events/url_verification for details. + Refer to https://api.slack.com/events/url_verification for details. Args: base_logger: The base logger @@ -965,7 +965,7 @@

    Inherited members

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://docs.slack.dev/reference/events/url_verification for details.

    +

    Refer to https://api.slack.com/events/url_verification for details.

    Args

    base_logger
    @@ -1077,7 +1077,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/message_listener_matches/async_message_listener_matches.html b/docs/reference/middleware/message_listener_matches/async_message_listener_matches.html index 790669551..9cbee09ca 100644 --- a/docs/reference/middleware/message_listener_matches/async_message_listener_matches.html +++ b/docs/reference/middleware/message_listener_matches/async_message_listener_matches.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.message_listener_matches.async_message_listener_matches API documentation @@ -124,7 +124,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/message_listener_matches/index.html b/docs/reference/middleware/message_listener_matches/index.html index 340bfca8b..29dfbb861 100644 --- a/docs/reference/middleware/message_listener_matches/index.html +++ b/docs/reference/middleware/message_listener_matches/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.message_listener_matches API documentation @@ -141,7 +141,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/message_listener_matches/message_listener_matches.html b/docs/reference/middleware/message_listener_matches/message_listener_matches.html index 67481a683..35b5bfa7a 100644 --- a/docs/reference/middleware/message_listener_matches/message_listener_matches.html +++ b/docs/reference/middleware/message_listener_matches/message_listener_matches.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.message_listener_matches.message_listener_matches API documentation @@ -124,7 +124,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/middleware.html b/docs/reference/middleware/middleware.html index f2940d31b..fb05fd3cc 100644 --- a/docs/reference/middleware/middleware.html +++ b/docs/reference/middleware/middleware.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.middleware API documentation @@ -232,7 +232,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/middleware_error_handler.html b/docs/reference/middleware/middleware_error_handler.html index 8dcce6026..168fd0409 100644 --- a/docs/reference/middleware/middleware_error_handler.html +++ b/docs/reference/middleware/middleware_error_handler.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.middleware_error_handler API documentation @@ -234,7 +234,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/request_verification/async_request_verification.html b/docs/reference/middleware/request_verification/async_request_verification.html index dfa1b22bf..dc2b20908 100644 --- a/docs/reference/middleware/request_verification/async_request_verification.html +++ b/docs/reference/middleware/request_verification/async_request_verification.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.request_verification.async_request_verification API documentation @@ -59,7 +59,7 @@

    Classes

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details. + Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. """ async def async_process( @@ -86,10 +86,10 @@

    Classes

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    Args

    signing_secret
    @@ -142,7 +142,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/request_verification/index.html b/docs/reference/middleware/request_verification/index.html index c841a74dc..ec8e6b941 100644 --- a/docs/reference/middleware/request_verification/index.html +++ b/docs/reference/middleware/request_verification/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.request_verification API documentation @@ -71,7 +71,7 @@

    Classes

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details. + Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. Args: signing_secret: The signing secret @@ -120,7 +120,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    Args

    signing_secret
    @@ -176,7 +176,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/request_verification/request_verification.html b/docs/reference/middleware/request_verification/request_verification.html index 4c778807a..aa5da095f 100644 --- a/docs/reference/middleware/request_verification/request_verification.html +++ b/docs/reference/middleware/request_verification/request_verification.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.request_verification.request_verification API documentation @@ -60,7 +60,7 @@

    Classes

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details. + Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. Args: signing_secret: The signing secret @@ -109,7 +109,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    Args

    signing_secret
    @@ -159,7 +159,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/ssl_check/async_ssl_check.html b/docs/reference/middleware/ssl_check/async_ssl_check.html index a8d6bbfcd..eaacf0846 100644 --- a/docs/reference/middleware/ssl_check/async_ssl_check.html +++ b/docs/reference/middleware/ssl_check/async_ssl_check.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.ssl_check.async_ssl_check API documentation @@ -75,12 +75,12 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles ssl_check requests. -Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details.

    +Refer to https://api.slack.com/interactivity/slash-commands for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    base_logger
    The base logger
    @@ -131,7 +131,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/ssl_check/index.html b/docs/reference/middleware/ssl_check/index.html index fd800326a..6a6477071 100644 --- a/docs/reference/middleware/ssl_check/index.html +++ b/docs/reference/middleware/ssl_check/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.ssl_check API documentation @@ -76,11 +76,11 @@

    Classes

    base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details. + Refer to https://api.slack.com/interactivity/slash-commands for details. Args: verification_token: The verification token to check - (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation) + (optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token @@ -122,12 +122,12 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles slack_bolt.middleware.ssl_check.ssl_check requests. -Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details.

    +Refer to https://api.slack.com/interactivity/slash-commands for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    base_logger
    The base logger
    @@ -194,7 +194,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/ssl_check/ssl_check.html b/docs/reference/middleware/ssl_check/ssl_check.html index 5d34eb280..72b98724a 100644 --- a/docs/reference/middleware/ssl_check/ssl_check.html +++ b/docs/reference/middleware/ssl_check/ssl_check.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.ssl_check.ssl_check API documentation @@ -65,11 +65,11 @@

    Classes

    base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. - Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details. + Refer to https://api.slack.com/interactivity/slash-commands for details. Args: verification_token: The verification token to check - (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation) + (optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token @@ -111,12 +111,12 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles ssl_check requests. -Refer to https://docs.slack.dev/interactivity/implementing-slash-commands for details.

    +Refer to https://api.slack.com/interactivity/slash-commands for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    base_logger
    The base logger
    @@ -177,7 +177,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/url_verification/async_url_verification.html b/docs/reference/middleware/url_verification/async_url_verification.html index 460aecf4d..e7fbb82fe 100644 --- a/docs/reference/middleware/url_verification/async_url_verification.html +++ b/docs/reference/middleware/url_verification/async_url_verification.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.url_verification.async_url_verification API documentation @@ -73,7 +73,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://docs.slack.dev/reference/events/url_verification for details.

    +

    Refer to https://api.slack.com/events/url_verification for details.

    Args

    base_logger
    @@ -124,7 +124,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/middleware/url_verification/index.html b/docs/reference/middleware/url_verification/index.html index e3e98f95f..9e08c1699 100644 --- a/docs/reference/middleware/url_verification/index.html +++ b/docs/reference/middleware/url_verification/index.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.url_verification API documentation @@ -70,7 +70,7 @@

    Classes

    def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. - Refer to https://docs.slack.dev/reference/events/url_verification for details. + Refer to https://api.slack.com/events/url_verification for details. Args: base_logger: The base logger @@ -104,7 +104,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://docs.slack.dev/reference/events/url_verification for details.

    +

    Refer to https://api.slack.com/events/url_verification for details.

    Args

    base_logger
    @@ -158,7 +158,7 @@

    diff --git a/docs/reference/middleware/url_verification/url_verification.html b/docs/reference/middleware/url_verification/url_verification.html index 32dc64d49..e90bf0395 100644 --- a/docs/reference/middleware/url_verification/url_verification.html +++ b/docs/reference/middleware/url_verification/url_verification.html @@ -3,7 +3,7 @@ - + slack_bolt.middleware.url_verification.url_verification API documentation @@ -59,7 +59,7 @@

    Classes

    def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. - Refer to https://docs.slack.dev/reference/events/url_verification for details. + Refer to https://api.slack.com/events/url_verification for details. Args: base_logger: The base logger @@ -93,7 +93,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://docs.slack.dev/reference/events/url_verification for details.

    +

    Refer to https://api.slack.com/events/url_verification for details.

    Args

    base_logger
    @@ -141,7 +141,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/oauth/async_callback_options.html b/docs/reference/oauth/async_callback_options.html index 40ae06c1f..822867ea8 100644 --- a/docs/reference/oauth/async_callback_options.html +++ b/docs/reference/oauth/async_callback_options.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.async_callback_options API documentation @@ -279,7 +279,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/oauth/async_internals.html b/docs/reference/oauth/async_internals.html index ba6a31642..2b35a69c9 100644 --- a/docs/reference/oauth/async_internals.html +++ b/docs/reference/oauth/async_internals.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.async_internals API documentation @@ -120,7 +120,7 @@

    Functions

    diff --git a/docs/reference/oauth/async_oauth_flow.html b/docs/reference/oauth/async_oauth_flow.html index 182973db5..3ccdfd6f0 100644 --- a/docs/reference/oauth/async_oauth_flow.html +++ b/docs/reference/oauth/async_oauth_flow.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.async_oauth_flow API documentation @@ -803,7 +803,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/oauth/async_oauth_settings.html b/docs/reference/oauth/async_oauth_settings.html index 18b108491..5e6a543c4 100644 --- a/docs/reference/oauth/async_oauth_settings.html +++ b/docs/reference/oauth/async_oauth_settings.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.async_oauth_settings API documentation @@ -417,7 +417,7 @@

    diff --git a/docs/reference/oauth/callback_options.html b/docs/reference/oauth/callback_options.html index ac2a200b1..7ad3734b3 100644 --- a/docs/reference/oauth/callback_options.html +++ b/docs/reference/oauth/callback_options.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.callback_options API documentation @@ -299,7 +299,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/oauth/index.html b/docs/reference/oauth/index.html index 6281fbcc1..d118a5e72 100644 --- a/docs/reference/oauth/index.html +++ b/docs/reference/oauth/index.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth API documentation @@ -856,7 +856,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/oauth/internals.html b/docs/reference/oauth/internals.html index e1b239782..3f1b43a7e 100644 --- a/docs/reference/oauth/internals.html +++ b/docs/reference/oauth/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.internals API documentation @@ -225,7 +225,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/oauth/oauth_flow.html b/docs/reference/oauth/oauth_flow.html index abae18cb5..75aa3cb88 100644 --- a/docs/reference/oauth/oauth_flow.html +++ b/docs/reference/oauth/oauth_flow.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.oauth_flow API documentation @@ -807,7 +807,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/oauth/oauth_settings.html b/docs/reference/oauth/oauth_settings.html index 0555b761c..1eb2ab7dd 100644 --- a/docs/reference/oauth/oauth_settings.html +++ b/docs/reference/oauth/oauth_settings.html @@ -3,7 +3,7 @@ - + slack_bolt.oauth.oauth_settings API documentation @@ -415,7 +415,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/request/async_internals.html b/docs/reference/request/async_internals.html index 635bdf586..35a250c8d 100644 --- a/docs/reference/request/async_internals.html +++ b/docs/reference/request/async_internals.html @@ -3,7 +3,7 @@ - + slack_bolt.request.async_internals API documentation @@ -129,7 +129,7 @@

    Functions

    diff --git a/docs/reference/request/async_request.html b/docs/reference/request/async_request.html index c08c47ec9..a3658710a 100644 --- a/docs/reference/request/async_request.html +++ b/docs/reference/request/async_request.html @@ -3,7 +3,7 @@ - + slack_bolt.request.async_request API documentation @@ -238,7 +238,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/request/index.html b/docs/reference/request/index.html index 61908105d..06d9b4933 100644 --- a/docs/reference/request/index.html +++ b/docs/reference/request/index.html @@ -3,7 +3,7 @@ - + slack_bolt.request API documentation @@ -37,7 +37,7 @@

    Module slack_bolt.request

    Incoming request from Slack through either HTTP request or Socket Mode connection.

    -

    Refer to https://docs.slack.dev/apis/events-api/ for the two types of connections. +

    Refer to https://api.slack.com/apis/connections for the two types of connections. This interface encapsulates the difference between the two.

    @@ -272,7 +272,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/request/internals.html b/docs/reference/request/internals.html index 68fa34dae..bc13932ec 100644 --- a/docs/reference/request/internals.html +++ b/docs/reference/request/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.request.internals API documentation @@ -601,7 +601,7 @@

    Functions

    diff --git a/docs/reference/request/payload_utils.html b/docs/reference/request/payload_utils.html index 21490bdc8..4fe75fd81 100644 --- a/docs/reference/request/payload_utils.html +++ b/docs/reference/request/payload_utils.html @@ -3,7 +3,7 @@ - + slack_bolt.request.payload_utils API documentation @@ -622,7 +622,7 @@

    Functions

    diff --git a/docs/reference/request/request.html b/docs/reference/request/request.html index dfd0fa3f1..870b65f08 100644 --- a/docs/reference/request/request.html +++ b/docs/reference/request/request.html @@ -3,7 +3,7 @@ - + slack_bolt.request.request API documentation @@ -237,7 +237,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/response/index.html b/docs/reference/response/index.html index bc86a6770..c986c7150 100644 --- a/docs/reference/response/index.html +++ b/docs/reference/response/index.html @@ -3,7 +3,7 @@ - + slack_bolt.response API documentation @@ -39,7 +39,7 @@

    Module slack_bolt.response

    This interface represents Bolt's synchronous response to Slack.

    In Socket Mode, the response data can be transformed to a WebSocket message. In the HTTP endpoint mode, the response data becomes an HTTP response data.

    -

    Refer to https://docs.slack.dev/apis/events-api/ for the two types of connections.

    +

    Refer to https://api.slack.com/apis/connections for the two types of connections.

    Sub-modules

    @@ -227,7 +227,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/response/response.html b/docs/reference/response/response.html index 65151cf64..5044254e8 100644 --- a/docs/reference/response/response.html +++ b/docs/reference/response/response.html @@ -3,7 +3,7 @@ - + slack_bolt.response.response API documentation @@ -211,7 +211,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/util/async_utils.html b/docs/reference/util/async_utils.html index f7ce77489..f74d8f0ac 100644 --- a/docs/reference/util/async_utils.html +++ b/docs/reference/util/async_utils.html @@ -3,7 +3,7 @@ - + slack_bolt.util.async_utils API documentation @@ -85,7 +85,7 @@

    Functions

    diff --git a/docs/reference/util/index.html b/docs/reference/util/index.html index 54f1dbd8d..6eadaacb9 100644 --- a/docs/reference/util/index.html +++ b/docs/reference/util/index.html @@ -3,7 +3,7 @@ - + slack_bolt.util API documentation @@ -78,7 +78,7 @@

    Sub-modules

    diff --git a/docs/reference/util/utils.html b/docs/reference/util/utils.html index d22c1f581..33e6b1de2 100644 --- a/docs/reference/util/utils.html +++ b/docs/reference/util/utils.html @@ -3,7 +3,7 @@ - + slack_bolt.util.utils API documentation @@ -268,7 +268,7 @@

    Returns

    diff --git a/docs/reference/version.html b/docs/reference/version.html index 404f38d79..c4a0f9b83 100644 --- a/docs/reference/version.html +++ b/docs/reference/version.html @@ -3,7 +3,7 @@ - + slack_bolt.version API documentation @@ -61,7 +61,7 @@

    Module slack_bolt.version

    diff --git a/docs/reference/workflows/index.html b/docs/reference/workflows/index.html index 3e43b0701..caaffe74d 100644 --- a/docs/reference/workflows/index.html +++ b/docs/reference/workflows/index.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows API documentation @@ -43,7 +43,7 @@

    Module slack_bolt.workflows

  • slack_bolt.workflows.step.utilities
  • slack_bolt.workflows.step.async_step (if you use asyncio-based AsyncApp)
  • -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +

    Refer to https://api.slack.com/workflows/steps for details.

    Sub-modules

    @@ -80,7 +80,7 @@

    Sub-modules

    diff --git a/docs/reference/workflows/step/async_step.html b/docs/reference/workflows/step/async_step.html index 0c08a9707..3bf597134 100644 --- a/docs/reference/workflows/step/async_step.html +++ b/docs/reference/workflows/step/async_step.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.async_step API documentation @@ -78,7 +78,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Args: callback_id: The callback_id for this step from app @@ -124,7 +124,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt """ return AsyncWorkflowStepBuilder(callback_id, base_logger=base_logger) @@ -200,7 +200,7 @@

    Classes

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Args

    callback_id
    @@ -252,7 +252,7 @@

    Static methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    @@ -267,7 +267,7 @@

    Static methods

    class AsyncWorkflowStepBuilder:
         """Steps from apps
    -    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.
    +    Refer to https://api.slack.com/workflows/steps for details.
         """
     
         callback_id: Union[str, Pattern]
    @@ -285,7 +285,7 @@ 

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt This builder is supposed to be used as decorator. @@ -327,7 +327,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new edit listener with details. @@ -380,7 +380,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new save listener with details. @@ -433,7 +433,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new execute listener with details. @@ -480,7 +480,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -555,10 +555,10 @@

    Static methods

    return _middleware

    Steps from apps -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/workflows/steps for details.

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    This builder is supposed to be used as decorator.

    my_step = AsyncWorkflowStep.builder("my_step")
     @my_step.edit
    @@ -659,7 +659,7 @@ 

    Methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -685,7 +685,7 @@

    Methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object.

    Returns

    @@ -709,7 +709,7 @@

    Returns

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new edit listener with details. @@ -754,7 +754,7 @@

    Returns

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new edit listener with details.

    You can use this method as decorator as well.

    @my_step.edit
    @@ -799,7 +799,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new execute listener with details. @@ -844,7 +844,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new execute listener with details.

    You can use this method as decorator as well.

    @my_step.execute
    @@ -889,7 +889,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new save listener with details. @@ -934,7 +934,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new save listener with details.

    You can use this method as decorator as well.

    @my_step.save
    @@ -1007,7 +1007,7 @@ 

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/async_step_middleware.html b/docs/reference/workflows/step/async_step_middleware.html index fff1cda5c..a174b9c11 100644 --- a/docs/reference/workflows/step/async_step_middleware.html +++ b/docs/reference/workflows/step/async_step_middleware.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.async_step_middleware API documentation @@ -140,7 +140,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/index.html b/docs/reference/workflows/step/index.html index ce29a210f..62d989976 100644 --- a/docs/reference/workflows/step/index.html +++ b/docs/reference/workflows/step/index.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step API documentation @@ -103,7 +103,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepCompleted API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.stepCompleted for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -135,7 +135,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepCompleted API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.stepCompleted for details.

    class Configure @@ -174,7 +174,7 @@

    Classes

    ) app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/workflows/steps for details. """ def __init__(self, *, callback_id: str, client: WebClient, body: dict): @@ -219,7 +219,7 @@

    Classes

    ) app.step(ws)
    -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +

    Refer to https://api.slack.com/workflows/steps for details.

    class Fail @@ -248,7 +248,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.stepFailed for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -281,7 +281,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.stepFailed for details.

    class Update @@ -329,7 +329,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.updateStep for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -377,7 +377,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.updateStep for details.

    class WorkflowStep @@ -411,7 +411,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Args: callback_id: The callback_id for this step from app @@ -453,7 +453,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt """ return WorkflowStepBuilder( callback_id, @@ -546,7 +546,7 @@

    Classes

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Args

    callback_id
    @@ -598,7 +598,7 @@

    Static methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    @@ -732,7 +732,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/internals.html b/docs/reference/workflows/step/internals.html index f2067677f..c5fda1012 100644 --- a/docs/reference/workflows/step/internals.html +++ b/docs/reference/workflows/step/internals.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.internals API documentation @@ -60,7 +60,7 @@

    Module slack_bolt.workflows.step.internals

    diff --git a/docs/reference/workflows/step/step.html b/docs/reference/workflows/step/step.html index efaaad899..6e1567bd6 100644 --- a/docs/reference/workflows/step/step.html +++ b/docs/reference/workflows/step/step.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.step API documentation @@ -78,7 +78,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Args: callback_id: The callback_id for this step from app @@ -120,7 +120,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt """ return WorkflowStepBuilder( callback_id, @@ -213,7 +213,7 @@

    Classes

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Args

    callback_id
    @@ -265,7 +265,7 @@

    Static methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    @@ -280,7 +280,7 @@

    Static methods

    class WorkflowStepBuilder:
         """Steps from apps
    -    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.
    +    Refer to https://api.slack.com/workflows/steps for details.
         """
     
         callback_id: Union[str, Pattern]
    @@ -298,7 +298,7 @@ 

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt This builder is supposed to be used as decorator. @@ -340,7 +340,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new edit listener with details. @@ -394,7 +394,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new save listener with details. @@ -447,7 +447,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new execute listener with details. @@ -494,7 +494,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -584,10 +584,10 @@

    Static methods

    return _middleware

    Steps from apps -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/workflows/steps for details.

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    This builder is supposed to be used as decorator.

    my_step = WorkflowStep.builder("my_step")
     @my_step.edit
    @@ -703,7 +703,7 @@ 

    Methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -729,7 +729,7 @@

    Methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object.

    Returns

    @@ -753,7 +753,7 @@

    Returns

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new edit listener with details. @@ -799,7 +799,7 @@

    Returns

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new edit listener with details.

    You can use this method as decorator as well.

    @my_step.edit
    @@ -844,7 +844,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new execute listener with details. @@ -889,7 +889,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new execute listener with details.

    You can use this method as decorator as well.

    @my_step.execute
    @@ -934,7 +934,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://docs.slack.dev/workflows/workflow-steps + Use new custom steps: https://api.slack.com/automation/functions/custom-bolt Registers a new save listener with details. @@ -979,7 +979,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://docs.slack.dev/workflows/workflow-steps

    +Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    Registers a new save listener with details.

    You can use this method as decorator as well.

    @my_step.save
    @@ -1052,7 +1052,7 @@ 

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/step_middleware.html b/docs/reference/workflows/step/step_middleware.html index 58a1c6194..2ac62dd93 100644 --- a/docs/reference/workflows/step/step_middleware.html +++ b/docs/reference/workflows/step/step_middleware.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.step_middleware API documentation @@ -143,7 +143,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/utilities/async_complete.html b/docs/reference/workflows/step/utilities/async_complete.html index 8a8790900..8e95cc267 100644 --- a/docs/reference/workflows/step/utilities/async_complete.html +++ b/docs/reference/workflows/step/utilities/async_complete.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.async_complete API documentation @@ -76,7 +76,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepCompleted API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.stepCompleted for details. """ def __init__(self, *, client: AsyncWebClient, body: dict): @@ -108,7 +108,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepCompleted API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.stepCompleted for details.

    @@ -134,7 +134,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/utilities/async_configure.html b/docs/reference/workflows/step/utilities/async_configure.html index 3151e71bd..008c35ab5 100644 --- a/docs/reference/workflows/step/utilities/async_configure.html +++ b/docs/reference/workflows/step/utilities/async_configure.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.async_configure API documentation @@ -83,7 +83,7 @@

    Classes

    ) app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/workflows/steps for details. """ def __init__(self, *, callback_id: str, client: AsyncWebClient, body: dict): @@ -131,7 +131,7 @@

    Classes

    ) app.step(ws)
    -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +

    Refer to https://api.slack.com/workflows/steps for details.

    @@ -157,7 +157,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/utilities/async_fail.html b/docs/reference/workflows/step/utilities/async_fail.html index 1206c45a6..b27c36251 100644 --- a/docs/reference/workflows/step/utilities/async_fail.html +++ b/docs/reference/workflows/step/utilities/async_fail.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.async_fail API documentation @@ -73,7 +73,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.stepFailed for details. """ def __init__(self, *, client: AsyncWebClient, body: dict): @@ -106,7 +106,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.stepFailed for details.

    @@ -132,7 +132,7 @@

    diff --git a/docs/reference/workflows/step/utilities/async_update.html b/docs/reference/workflows/step/utilities/async_update.html index 0d1cad162..bfb210fc3 100644 --- a/docs/reference/workflows/step/utilities/async_update.html +++ b/docs/reference/workflows/step/utilities/async_update.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.async_update API documentation @@ -92,7 +92,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.updateStep for details. """ def __init__(self, *, client: AsyncWebClient, body: dict): @@ -140,7 +140,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.updateStep for details.

    @@ -166,7 +166,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/utilities/complete.html b/docs/reference/workflows/step/utilities/complete.html index c15d51e9c..f1cf11f56 100644 --- a/docs/reference/workflows/step/utilities/complete.html +++ b/docs/reference/workflows/step/utilities/complete.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.complete API documentation @@ -76,7 +76,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepCompleted API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.stepCompleted for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -108,7 +108,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepCompleted API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.stepCompleted for details.

    @@ -134,7 +134,7 @@

    diff --git a/docs/reference/workflows/step/utilities/configure.html b/docs/reference/workflows/step/utilities/configure.html index 2c1aeadbf..26d646cf2 100644 --- a/docs/reference/workflows/step/utilities/configure.html +++ b/docs/reference/workflows/step/utilities/configure.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.configure API documentation @@ -83,7 +83,7 @@

    Classes

    ) app.step(ws) - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/workflows/steps for details. """ def __init__(self, *, callback_id: str, client: WebClient, body: dict): @@ -128,7 +128,7 @@

    Classes

    ) app.step(ws)
    -

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +

    Refer to https://api.slack.com/workflows/steps for details.

    @@ -154,7 +154,7 @@

    diff --git a/docs/reference/workflows/step/utilities/fail.html b/docs/reference/workflows/step/utilities/fail.html index 4c4091fd5..00d0be83d 100644 --- a/docs/reference/workflows/step/utilities/fail.html +++ b/docs/reference/workflows/step/utilities/fail.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.fail API documentation @@ -73,7 +73,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.stepFailed for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -106,7 +106,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.stepFailed for details.

    @@ -132,7 +132,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/docs/reference/workflows/step/utilities/index.html b/docs/reference/workflows/step/utilities/index.html index 1594bacb6..54261ea96 100644 --- a/docs/reference/workflows/step/utilities/index.html +++ b/docs/reference/workflows/step/utilities/index.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities API documentation @@ -127,7 +127,7 @@

    Sub-modules

    diff --git a/docs/reference/workflows/step/utilities/update.html b/docs/reference/workflows/step/utilities/update.html index c93fc7f21..9899448f9 100644 --- a/docs/reference/workflows/step/utilities/update.html +++ b/docs/reference/workflows/step/utilities/update.html @@ -3,7 +3,7 @@ - + slack_bolt.workflows.step.utilities.update API documentation @@ -92,7 +92,7 @@

    Classes

    app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. - Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. + Refer to https://api.slack.com/methods/workflows.updateStep for details. """ def __init__(self, *, client: WebClient, body: dict): @@ -140,7 +140,7 @@

    Classes

    app.step(ws)

    This utility is a thin wrapper of workflows.stepFailed API method. -Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    +Refer to https://api.slack.com/methods/workflows.updateStep for details.

    @@ -166,7 +166,7 @@

    -

    Generated by pdoc 0.11.5.

    +

    Generated by pdoc 0.11.6.

    diff --git a/scripts/generate_api_docs.sh b/scripts/generate_api_docs.sh index 459476122..f8ea39d0d 100755 --- a/scripts/generate_api_docs.sh +++ b/scripts/generate_api_docs.sh @@ -6,5 +6,9 @@ cd ${script_dir}/.. pip install -U pdoc3 rm -rf docs/reference -pdoc reference --html -o docs + +pdoc slack_bolt --html -o docs/reference +cp -R docs/reference/slack_bolt/* docs/reference/ +rm -rf docs/reference/slack_bolt + open docs/reference/index.html diff --git a/slack_bolt/version.py b/slack_bolt/version.py index 03d768130..b996e1572 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.23.0" +__version__ = "1.24.0" From 9e0b3ed2665d6bfee58f388d3879631e8ae102f3 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Mon, 8 Sep 2025 10:35:44 -0700 Subject: [PATCH 137/282] docs: updates gif and enterprise paths (#1358) --- docs/english/concepts/authenticating-oauth.md | 2 +- docs/english/tutorial/modals/modals.md | 2 +- docs/img/announce.gif | Bin 0 -> 260652 bytes docs/japanese/concepts/authenticating-oauth.md | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 docs/img/announce.gif diff --git a/docs/english/concepts/authenticating-oauth.md b/docs/english/concepts/authenticating-oauth.md index 88b422949..4ce9b205b 100644 --- a/docs/english/concepts/authenticating-oauth.md +++ b/docs/english/concepts/authenticating-oauth.md @@ -6,7 +6,7 @@ Bolt for Python will create a **Redirect URL** `slack/oauth_redirect`, which Sla Bolt for Python will also create a `slack/install` route, where you can find an **Add to Slack** button for your app to perform direct installs of your app. If you need any additional authorizations (user tokens) from users inside a team when your app is already installed or a reason to dynamically generate an install URL, you can pass your own custom URL generator to `oauth_settings` as `authorize_url_generator`. -Bolt for Python automatically includes support for [org wide installations](/enterprise-grid/) in version `1.1.0+`. Org wide installations can be enabled in your app configuration settings under **Org Level Apps**. +Bolt for Python automatically includes support for [org wide installations](/enterprise) in version `1.1.0+`. Org wide installations can be enabled in your app configuration settings under **Org Level Apps**. To learn more about the OAuth installation flow with Slack, [read the API documentation](/authentication/installing-with-oauth). diff --git a/docs/english/tutorial/modals/modals.md b/docs/english/tutorial/modals/modals.md index ee6d1e0d8..d25470b97 100644 --- a/docs/english/tutorial/modals/modals.md +++ b/docs/english/tutorial/modals/modals.md @@ -9,7 +9,7 @@ GitHub Codespaces is an online IDE that allows you to work on code and host your At the end of this tutorial, your final app will look like this: -![announce](https://github.com/user-attachments/assets/0bf1c2f0-4b22-4c9c-98b3-b21e9bcc14a8) +![announce](/img/bolt-python/announce.gif) And will make use of these Slack concepts: * [**Block Kit**](/block-kit/) is a UI framework for Slack apps that allows you to create beautiful, interactive messages within Slack. If you've ever seen a message in Slack with buttons or a select menu, that's Block Kit. diff --git a/docs/img/announce.gif b/docs/img/announce.gif new file mode 100644 index 0000000000000000000000000000000000000000..7602784cead5bdc59bd1cb94e45e9f7300856f85 GIT binary patch literal 260652 zcmV($K;yqhNk%w1VT1!D0(SraA^!_bMO0HmK~P09E-(WD0000X`2+wP0000i00000 zgaaf3hXVov5)29w4G+IABp3}O8x}Gi86h4aC?gs+CKx+08Yd; zU<3j~J48S{WKBXUNkTJOM=DrIHD6FITTwh@Oe|zeIAK#SX<0aQNjh^`Id@|`NJmIb zOHx!zM^H{sSyM(>S6E(NU|eKVVNOY4Oi*cBLu6l7d`m=kSweYQUSeHgZD2xbWLbG< zM0#gce{e*5a#d$%XKZY8Yj|vZU~O=2aCdfh6J?`6T8BJik1BJiL0W};e1;c-WH^#| zB%*RJw0<+Ec`&nmM5%u}gRecZgEhqPNSlI0u7pXqidCYFUZ|5%w2NN3lz)JLZnK

    P%vJ%w#Nh+9a7X-0@@T8VZ_i+NR)X-kx4 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

    U`-6DC-f*y~5oPR*T)5dc^}u;Iin9<)b@V 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Ó!exVjZiNQatMKlRS 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�Uhvd3tEy81|&dobZr$j{Am#7 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 Date: Tue, 9 Sep 2025 09:56:26 -0400 Subject: [PATCH 138/282] chore: remove 3.6 support (#1359) --- .github/maintainers_guide.md | 4 ++-- README.md | 4 ++-- docs/english/building-an-app.md | 2 +- docs/english/getting-started.md | 4 ++-- docs/english/tutorial/ai-chatbot/ai-chatbot.md | 2 +- .../custom-steps-for-jira/custom-steps-for-jira.md | 2 +- docs/japanese/getting-started.md | 2 +- pyproject.toml | 5 ++--- requirements/adapter.txt | 6 ++---- requirements/adapter_testing.txt | 2 +- requirements/tools.txt | 2 +- scripts/format.sh | 10 ++++++++++ scripts/install_all_and_run_tests.sh | 8 +------- slack_bolt/async_app.py | 2 +- slack_bolt/listener_matcher/builtins.py | 10 +++------- slack_bolt/logger/messages.py | 2 +- slack_bolt/util/utils.py | 14 +------------- 17 files changed, 33 insertions(+), 48 deletions(-) create mode 100755 scripts/format.sh diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index 69026d602..352398072 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -25,8 +25,8 @@ $ pyenv local 3.8.5 $ pyenv versions system - 3.6.10 - 3.7.7 + 3.7.17 + 3.13.7 * 3.8.5 (set by /path-to-bolt-python/.python-version) $ pyenv rehash diff --git a/README.md b/README.md index 10a44a0e5..b3f78adb0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A Python framework to build Slack apps in a flash with the latest platform featu ## Setup ```bash -# Python 3.6+ required +# Python 3.7+ required python -m venv .venv source .venv/bin/activate @@ -153,7 +153,7 @@ Most of the app's functionality will be inside listener functions (the `fn` para If you'd prefer to build your app with [asyncio](https://docs.python.org/3/library/asyncio.html), you can import the [AIOHTTP](https://docs.aiohttp.org/en/stable/) library and call the `AsyncApp` constructor. Within async apps, you can use the async/await pattern. ```bash -# Python 3.6+ required +# Python 3.7+ required python -m venv .venv source .venv/bin/activate diff --git a/docs/english/building-an-app.md b/docs/english/building-an-app.md index 87b26163b..301cc52c6 100644 --- a/docs/english/building-an-app.md +++ b/docs/english/building-an-app.md @@ -72,7 +72,7 @@ $ mkdir first-bolt-app $ cd first-bolt-app ``` -Next, we recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.6 or later](https://www.python.org/downloads/): +Next, we recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.7 or later](https://www.python.org/downloads/): ```sh $ python3 -m venv .venv diff --git a/docs/english/getting-started.md b/docs/english/getting-started.md index ebdf47189..a198736bc 100644 --- a/docs/english/getting-started.md +++ b/docs/english/getting-started.md @@ -21,7 +21,7 @@ In search of the complete guide to building an app from scratch? Check out the [ A few tools are needed for the following steps. We recommend using the [**Slack CLI**](/tools/slack-cli/) for the smoothest experience, but other options remain available. -You can also begin by installing git and downloading [Python 3.6 or later](https://www.python.org/downloads/), or the latest stable version of Python. Refer to [Python's setup and building guide](https://devguide.python.org/getting-started/setup-building/) for more details. +You can also begin by installing git and downloading [Python 3.7 or later](https://www.python.org/downloads/), or the latest stable version of Python. Refer to [Python's setup and building guide](https://devguide.python.org/getting-started/setup-building/) for more details. Install the latest version of the Slack CLI to get started: @@ -83,7 +83,7 @@ Outlines of a project are taking shape, so we can move on to running the app! -We recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.6 or later](https://www.python.org/downloads/): +We recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.7 or later](https://www.python.org/downloads/): ```sh $ python3 -m venv .venv diff --git a/docs/english/tutorial/ai-chatbot/ai-chatbot.md b/docs/english/tutorial/ai-chatbot/ai-chatbot.md index fa4da90a7..9da54c149 100644 --- a/docs/english/tutorial/ai-chatbot/ai-chatbot.md +++ b/docs/english/tutorial/ai-chatbot/ai-chatbot.md @@ -13,7 +13,7 @@ In this tutorial, you'll learn how to bring the power of AI into your Slack work Before getting started, you will need the following: * a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now — you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. -* a development environment with [Python 3.6](https://www.python.org/downloads/) or later. +* a development environment with [Python 3.7](https://www.python.org/downloads/) or later. * an Anthropic or OpenAI account with sufficient credits, and in which you have generated a secret key. **Skip to the code** diff --git a/docs/english/tutorial/custom-steps-for-jira/custom-steps-for-jira.md b/docs/english/tutorial/custom-steps-for-jira/custom-steps-for-jira.md index f310e75cc..d74b82b8e 100644 --- a/docs/english/tutorial/custom-steps-for-jira/custom-steps-for-jira.md +++ b/docs/english/tutorial/custom-steps-for-jira/custom-steps-for-jira.md @@ -12,7 +12,7 @@ In this tutorial, you'll learn how to configure custom steps for use with JIRA. Before getting started, you will need the following: * a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now—you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. -* a development environment with [Python 3.6](https://www.python.org/downloads/) or later. +* a development environment with [Python 3.7](https://www.python.org/downloads/) or later. **Skip to the code** If you'd rather skip the tutorial and just head straight to the code, you can use our [Bolt for Python JIRA functions sample](https://github.com/slack-samples/bolt-python-jira-functions) as a template. diff --git a/docs/japanese/getting-started.md b/docs/japanese/getting-started.md index 2ec34d193..30538bf94 100644 --- a/docs/japanese/getting-started.md +++ b/docs/japanese/getting-started.md @@ -64,7 +64,7 @@ mkdir first-bolt-app cd first-bolt-app ``` -次に、プロジェクトの依存ライブラリを管理する方法として、[Python 仮想環境](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment)を使ったおすすめの方法を紹介します。これはシステム Python に存在するパッケージとのコンフリクトを防ぐために推奨されている優れた方法です。[Python 3.6 以降](https://www.python.org/downloads/)の仮想環境を作成し、アクティブにしてみましょう。 +次に、プロジェクトの依存ライブラリを管理する方法として、[Python 仮想環境](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment)を使ったおすすめの方法を紹介します。これはシステム Python に存在するパッケージとのコンフリクトを防ぐために推奨されている優れた方法です。[Python 3.7 以降](https://www.python.org/downloads/)の仮想環境を作成し、アクティブにしてみましょう。 ```shell python3 -m venv .venv diff --git a/pyproject.toml b/pyproject.toml index 5ce2c62bc..07b338300 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "pytest-runner==6.0.1", "wheel"] +requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -8,7 +8,6 @@ dynamic = ["version", "readme", "dependencies", "authors"] description = "The Bolt Framework for Python" license = { text = "MIT" } classifiers = [ - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -20,7 +19,7 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] -requires-python = ">=3.6" +requires-python = ">=3.7" [project.urls] diff --git a/requirements/adapter.txt b/requirements/adapter.txt index b8cadb510..8fd5cd33e 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -3,8 +3,7 @@ # used only under slack_bolt/adapter boto3<=2 bottle>=0.12,<1 -chalice<=1.27.3; python_version=="3.6" -chalice>=1.28,<2; python_version>"3.6" +chalice>=1.28,<2; CherryPy>=18,<19 Django>=3,<6 falcon>=2,<5; python_version<"3.11" @@ -17,8 +16,7 @@ pyramid>=1,<3 # Sanic and its dependencies # Note: Sanic imports tracerite with wild card versions tracerite<1.1.2; python_version<="3.8" # older versions of python are not compatible with tracerite>1.1.2 -sanic>=20,<21; python_version=="3.6" -sanic>=21,<24; python_version>"3.6" and python_version<="3.8" +sanic>=21,<24; python_version<="3.8" sanic>=21,<26; python_version>"3.8" starlette>=0.19.1,<1 diff --git a/requirements/adapter_testing.txt b/requirements/adapter_testing.txt index 3d829ee15..c497a1f3f 100644 --- a/requirements/adapter_testing.txt +++ b/requirements/adapter_testing.txt @@ -2,4 +2,4 @@ moto>=3,<6 # For AWS tests docker>=5,<8 # Used by moto boddle>=0.2,<0.3 # For Bottle app tests -sanic-testing>=0.7; python_version>"3.6" +sanic-testing>=0.7 diff --git a/requirements/tools.txt b/requirements/tools.txt index c3a383a13..b4cf790b9 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ mypy==1.17.1 flake8==7.3.0 -black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version +black==25.1.0 diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100755 index 000000000..10987bdd6 --- /dev/null +++ b/scripts/format.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# ./scripts/format.sh + +script_dir=`dirname $0` +cd ${script_dir}/.. + +pip install -U pip +pip install -r requirements/tools.txt + +black slack_bolt/ tests/ diff --git a/scripts/install_all_and_run_tests.sh b/scripts/install_all_and_run_tests.sh index 21660dca5..a3154ae69 100755 --- a/scripts/install_all_and_run_tests.sh +++ b/scripts/install_all_and_run_tests.sh @@ -14,14 +14,8 @@ pip install -U pip pip uninstall python-lambda test_target="$1" -python_version=`python --version | awk '{print $2}'` -if [ ${python_version:0:3} == "3.6" ] -then - pip install -U -r requirements.txt -else - pip install -e . -fi +pip install -e . if [[ $test_target != "" ]] then diff --git a/slack_bolt/async_app.py b/slack_bolt/async_app.py index 10878c51b..fdf724d4c 100644 --- a/slack_bolt/async_app.py +++ b/slack_bolt/async_app.py @@ -5,7 +5,7 @@ If you'd prefer to build your app with [asyncio](https://docs.python.org/3/library/asyncio.html), you can import the [AIOHTTP](https://docs.aiohttp.org/en/stable/) library and call the `AsyncApp` constructor. Within async apps, you can use the async/await pattern. ```bash -# Python 3.6+ required +# Python 3.7+ required python -m venv .venv source .venv/bin/activate diff --git a/slack_bolt/listener_matcher/builtins.py b/slack_bolt/listener_matcher/builtins.py index fe06b9eef..57dbdf4f1 100644 --- a/slack_bolt/listener_matcher/builtins.py +++ b/slack_bolt/listener_matcher/builtins.py @@ -1,5 +1,4 @@ import re -import sys from logging import Logger from slack_bolt.error import BoltError @@ -25,10 +24,7 @@ from ..logger.messages import error_message_event_type from ..util.utils import get_arg_names_of_callable -if sys.version_info.major == 3 and sys.version_info.minor <= 6: - from re import _pattern_type as Pattern # type: ignore[attr-defined] -else: - from re import Pattern +from re import Pattern from typing import Callable, Awaitable, Any, Sequence, Optional, Union, Dict from slack_bolt.kwargs_injection import build_required_kwargs @@ -169,7 +165,7 @@ def _check_event_subtype(event_payload: dict, constraints: dict) -> bool: return True -def _verify_message_event_type(event_type: str) -> None: +def _verify_message_event_type(event_type: Union[str, Pattern]) -> None: if isinstance(event_type, str) and event_type.startswith("message."): raise ValueError(error_message_event_type(event_type)) if isinstance(event_type, Pattern) and "message\\." in event_type.pattern: @@ -324,7 +320,7 @@ def _block_action( elif isinstance(constraints, dict): # block_id matching is optional block_id: Optional[Union[str, Pattern]] = constraints.get("block_id") - action_id = constraints.get("action_id") + action_id = constraints.get("action_id") # type: ignore[assignment] if block_id is None and action_id is None: return False block_id_matched = block_id is None or _matches(block_id, action.get("block_id")) # type: ignore[union-attr] diff --git a/slack_bolt/logger/messages.py b/slack_bolt/logger/messages.py index cffdc445f..d30f51acb 100644 --- a/slack_bolt/logger/messages.py +++ b/slack_bolt/logger/messages.py @@ -60,7 +60,7 @@ def error_authorize_conflicts() -> str: return "`authorize` in the top-level arguments is not allowed when you pass either `oauth_settings` or `oauth_flow`" -def error_message_event_type(event_type: str) -> str: +def error_message_event_type(event_type: Union[str, Pattern]) -> str: return ( f'Although the document mentions "{event_type}", ' 'it is not a valid event type. Use "message" instead. ' diff --git a/slack_bolt/util/utils.py b/slack_bolt/util/utils.py index 0abdcfcbd..9ee313821 100644 --- a/slack_bolt/util/utils.py +++ b/slack_bolt/util/utils.py @@ -32,19 +32,7 @@ def convert_to_dict(obj: Union[Dict, JsonObject]) -> Dict: def create_copy(original: Any) -> Any: - if sys.version_info.major == 3 and sys.version_info.minor <= 6: - # NOTE: Unfortunately, copy.deepcopy doesn't work in Python 3.6.5. - # -------------------- - # > rv = reductor(4) - # E TypeError: can't pickle _thread.RLock objects - # ../../.pyenv/versions/3.6.10/lib/python3.6/copy.py:169: TypeError - # -------------------- - # As a workaround, this operation uses shallow copies in Python 3.6. - # If your code modifies the shared data in threads / async functions, race conditions may arise. - # Please consider upgrading Python major version to 3.7+ if you encounter some issues due to this. - return copy.copy(original) - else: - return copy.deepcopy(original) + return copy.deepcopy(original) def get_boot_message(development_server: bool = False) -> str: From a3adffaac15237e27f2522c60602697484431554 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 9 Sep 2025 13:33:43 -0400 Subject: [PATCH 139/282] chore: move dependencies to pyproject.toml (#1360) --- .github/dependabot.yml | 12 ------------ .github/workflows/tests.yml | 2 +- pyproject.toml | 4 ++-- requirements.txt | 1 - scripts/build_pypi_package.sh | 2 +- scripts/deploy_to_pypi_org.sh | 2 +- scripts/deploy_to_test_pypi_org.sh | 2 +- scripts/format.sh | 2 +- scripts/install_all_and_run_tests.sh | 2 +- scripts/run_flake8.sh | 2 +- scripts/run_mypy.sh | 8 ++++---- 11 files changed, 13 insertions(+), 26 deletions(-) delete mode 100644 requirements.txt diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 34b2ad725..dc523d227 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,15 +12,3 @@ updates: directory: "/" schedule: interval: "monthly" - - package-ecosystem: "npm" - directory: "/docs" - schedule: - interval: "monthly" - groups: - docusaurus: - patterns: - - "@docusaurus/*" - react: - patterns: - - "react" - - "react-dom" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f53a603ff..8ee9be411 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,7 +37,7 @@ jobs: - name: Install synchronous dependencies run: | pip install -U pip - pip install -r requirements.txt + pip install . pip install -r requirements/testing_without_asyncio.txt - name: Run tests without aiohttp run: | diff --git a/pyproject.toml b/pyproject.toml index 07b338300..024ee6654 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "slack_bolt" -dynamic = ["version", "readme", "dependencies", "authors"] +dynamic = ["version", "readme", "authors"] description = "The Bolt Framework for Python" license = { text = "MIT" } classifiers = [ @@ -20,6 +20,7 @@ classifiers = [ "Operating System :: OS Independent", ] requires-python = ">=3.7" +dependencies = ["slack_sdk>=3.35.0,<4"] [project.urls] @@ -31,7 +32,6 @@ include = ["slack_bolt*"] [tool.setuptools.dynamic] version = { attr = "slack_bolt.version.__version__" } readme = { file = ["README.md"], content-type = "text/markdown" } -dependencies = { file = ["requirements.txt"] } [tool.distutils.bdist_wheel] universal = true diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index c8d66106a..000000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -slack_sdk>=3.35.0,<4 diff --git a/scripts/build_pypi_package.sh b/scripts/build_pypi_package.sh index 79c6db9f2..5806262a6 100755 --- a/scripts/build_pypi_package.sh +++ b/scripts/build_pypi_package.sh @@ -5,7 +5,7 @@ cd ${script_dir}/.. rm -rf ./slack_bolt.egg-info pip install -U pip && \ - pip install twine build && \ + pip install -U twine build && \ rm -rf dist/ build/ slack_bolt.egg-info/ && \ python -m build --sdist --wheel && \ twine check dist/* diff --git a/scripts/deploy_to_pypi_org.sh b/scripts/deploy_to_pypi_org.sh index a3cf431fa..8c5234902 100755 --- a/scripts/deploy_to_pypi_org.sh +++ b/scripts/deploy_to_pypi_org.sh @@ -5,7 +5,7 @@ cd ${script_dir}/.. rm -rf ./slack_bolt.egg-info pip install -U pip && \ - pip install twine build && \ + pip install -U twine build && \ rm -rf dist/ build/ slack_bolt.egg-info/ && \ python -m build --sdist --wheel && \ twine check dist/* && \ diff --git a/scripts/deploy_to_test_pypi_org.sh b/scripts/deploy_to_test_pypi_org.sh index b2cc65a12..a6b9c352d 100644 --- a/scripts/deploy_to_test_pypi_org.sh +++ b/scripts/deploy_to_test_pypi_org.sh @@ -5,7 +5,7 @@ cd ${script_dir}/.. rm -rf ./slack_bolt.egg-info pip install -U pip && \ - pip install twine build && \ + pip install -U twine build && \ rm -rf dist/ build/ slack_bolt.egg-info/ && \ python -m build --sdist --wheel && \ twine check dist/* && \ diff --git a/scripts/format.sh b/scripts/format.sh index 10987bdd6..77cecf9e4 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -5,6 +5,6 @@ script_dir=`dirname $0` cd ${script_dir}/.. pip install -U pip -pip install -r requirements/tools.txt +pip install -U -r requirements/tools.txt black slack_bolt/ tests/ diff --git a/scripts/install_all_and_run_tests.sh b/scripts/install_all_and_run_tests.sh index a3154ae69..1f2690414 100755 --- a/scripts/install_all_and_run_tests.sh +++ b/scripts/install_all_and_run_tests.sh @@ -15,7 +15,7 @@ pip uninstall python-lambda test_target="$1" -pip install -e . +pip install -U -e . if [[ $test_target != "" ]] then diff --git a/scripts/run_flake8.sh b/scripts/run_flake8.sh index 73562da29..e523920f9 100755 --- a/scripts/run_flake8.sh +++ b/scripts/run_flake8.sh @@ -3,5 +3,5 @@ script_dir=$(dirname $0) cd ${script_dir}/.. && \ - pip install -r requirements/tools.txt && \ + pip install -U -r requirements/tools.txt && \ flake8 slack_bolt/ && flake8 examples/ diff --git a/scripts/run_mypy.sh b/scripts/run_mypy.sh index 77a2bacb7..c018443b7 100755 --- a/scripts/run_mypy.sh +++ b/scripts/run_mypy.sh @@ -3,8 +3,8 @@ script_dir=$(dirname $0) cd ${script_dir}/.. && \ - pip install . - pip install -r requirements/async.txt && \ - pip install -r requirements/adapter.txt && \ - pip install -r requirements/tools.txt && \ + pip install -U . + pip install -U -r requirements/async.txt && \ + pip install -U -r requirements/adapter.txt && \ + pip install -U -r requirements/tools.txt && \ mypy --config-file pyproject.toml From 3274d7a2b36101256ad498eee4aba794ab4ee888 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 9 Sep 2025 15:45:59 -0400 Subject: [PATCH 140/282] chore: version 1.25.0 (#1366) --- docs/reference/async_app.html | 2 +- docs/reference/logger/messages.html | 4 ++-- docs/reference/util/utils.html | 14 +------------- slack_bolt/version.py | 2 +- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/docs/reference/async_app.html b/docs/reference/async_app.html index 07c1f3627..ad9192253 100644 --- a/docs/reference/async_app.html +++ b/docs/reference/async_app.html @@ -39,7 +39,7 @@

    Module slack_bolt.async_app

    Module for creating asyncio based apps

    Creating an async app

    If you'd prefer to build your app with asyncio, you can import the AIOHTTP library and call the AsyncApp constructor. Within async apps, you can use the async/await pattern.

    -
    # Python 3.6+ required
    +
    # Python 3.7+ required
     python -m venv .venv
     source .venv/bin/activate
     
    diff --git a/docs/reference/logger/messages.html b/docs/reference/logger/messages.html
    index 3c8d67a31..85e0d94dd 100644
    --- a/docs/reference/logger/messages.html
    +++ b/docs/reference/logger/messages.html
    @@ -208,14 +208,14 @@ 

    Functions

    -def error_message_event_type(event_type: str) ‑> str +def error_message_event_type(event_type: str | re.Pattern) ‑> str
    Expand source code -
    def error_message_event_type(event_type: str) -> str:
    +
    def error_message_event_type(event_type: Union[str, Pattern]) -> str:
         return (
             f'Although the document mentions "{event_type}", '
             'it is not a valid event type. Use "message" instead. '
    diff --git a/docs/reference/util/utils.html b/docs/reference/util/utils.html
    index 33e6b1de2..85d336513 100644
    --- a/docs/reference/util/utils.html
    +++ b/docs/reference/util/utils.html
    @@ -83,19 +83,7 @@ 

    Functions

    Expand source code
    def create_copy(original: Any) -> Any:
    -    if sys.version_info.major == 3 and sys.version_info.minor <= 6:
    -        # NOTE: Unfortunately, copy.deepcopy doesn't work in Python 3.6.5.
    -        # --------------------
    -        # >     rv = reductor(4)
    -        # E     TypeError: can't pickle _thread.RLock objects
    -        # ../../.pyenv/versions/3.6.10/lib/python3.6/copy.py:169: TypeError
    -        # --------------------
    -        # As a workaround, this operation uses shallow copies in Python 3.6.
    -        # If your code modifies the shared data in threads / async functions, race conditions may arise.
    -        # Please consider upgrading Python major version to 3.7+ if you encounter some issues due to this.
    -        return copy.copy(original)
    -    else:
    -        return copy.deepcopy(original)
    + return copy.deepcopy(original)
    diff --git a/slack_bolt/version.py b/slack_bolt/version.py index b996e1572..7f9c19341 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.24.0" +__version__ = "1.25.0" From 420ec6bc4376890f892b2f51c6b92cac71d4978f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 20:00:34 +0000 Subject: [PATCH 141/282] chore(deps): update pytest-cov requirement from <7,>=3 to >=3,<8 (#1365) --- requirements/testing_without_asyncio.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/testing_without_asyncio.txt b/requirements/testing_without_asyncio.txt index d10c4345e..0e493f0e2 100644 --- a/requirements/testing_without_asyncio.txt +++ b/requirements/testing_without_asyncio.txt @@ -1,3 +1,3 @@ # pip install -r requirements/testing_without_asyncio.txt pytest>=6.2.5,<8.5 # https://github.com/tornadoweb/tornado/issues/3375 -pytest-cov>=3,<7 +pytest-cov>=3,<8 From 6f4fbf013ac796421347b6dc354c48577d6d8b6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 20:55:11 +0000 Subject: [PATCH 142/282] chore(deps): bump actions/setup-python from 5.6.0 to 6.0.0 (#1363) --- .github/workflows/codecov.yml | 2 +- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .github/workflows/tests.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 391c135c6..0eaf192ba 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -22,7 +22,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index 87f3496e1..ce6271c46 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -20,7 +20,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: ${{ matrix.python-version }} - name: Run flake8 verification diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index f333756b5..fd9ae0203 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -20,7 +20,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: ${{ matrix.python-version }} - name: Run mypy verification diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8ee9be411..8501cae36 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,7 +31,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: ${{ matrix.python-version }} - name: Install synchronous dependencies From f7114844960d3e4949c0d263510467f6256341fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:07:47 +0000 Subject: [PATCH 143/282] chore(deps): bump actions/checkout from 4.2.2 to 5.0.0 (#1362) --- .github/workflows/codecov.yml | 2 +- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .github/workflows/tests.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 0eaf192ba..01e6e6a5d 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -18,7 +18,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index ce6271c46..bd4e3dfd8 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index fd9ae0203..1bf4abf0d 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8501cae36..e68997aef 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,7 +27,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} From 055b320de04b394264cfb7361837af22b96dea0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:11:57 +0000 Subject: [PATCH 144/282] chore(deps): bump actions/stale from 9.1.0 to 10.0.0 (#1361) --- .github/workflows/triage-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index b37c13422..5cb75bf93 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -16,7 +16,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 + - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 with: days-before-issue-stale: 30 days-before-issue-close: 10 From c512c6b08d70f830932031b7d87485e23e9d9fc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:16:01 +0000 Subject: [PATCH 145/282] chore(deps): bump codecov/codecov-action from 5.4.3 to 5.5.1 (#1364) --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 01e6e6a5d..7381117bb 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -36,7 +36,7 @@ jobs: run: | pytest --cov=./slack_bolt/ --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: fail_ci_if_error: true verbose: true From 7cedaac2853d55d2329422c59a1c0bcec3b6ded0 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 15 Sep 2025 12:26:39 -0700 Subject: [PATCH 146/282] docs: add ai provider token instructions (#1340) --- .../english/tutorial/ai-chatbot/ai-chatbot.md | 69 ++++++++++++++----- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/docs/english/tutorial/ai-chatbot/ai-chatbot.md b/docs/english/tutorial/ai-chatbot/ai-chatbot.md index 9da54c149..72005f817 100644 --- a/docs/english/tutorial/ai-chatbot/ai-chatbot.md +++ b/docs/english/tutorial/ai-chatbot/ai-chatbot.md @@ -12,9 +12,9 @@ In this tutorial, you'll learn how to bring the power of AI into your Slack work Before getting started, you will need the following: -* a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now — you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. -* a development environment with [Python 3.7](https://www.python.org/downloads/) or later. -* an Anthropic or OpenAI account with sufficient credits, and in which you have generated a secret key. +- a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now — you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. +- a development environment with [Python 3.7](https://www.python.org/downloads/) or later. +- an Anthropic or OpenAI account with sufficient credits, and in which you have generated a secret key. **Skip to the code** If you'd rather skip the tutorial and just head straight to the code, you can use our [Bolt for Python AI Chatbot sample](https://github.com/slack-samples/bolt-python-ai-chatbot) as a template. @@ -25,31 +25,66 @@ If you'd rather skip the tutorial and just head straight to the code, you can us 2. Select the workspace you want to install the application in. 3. Copy the contents of the [`manifest.json`](https://github.com/slack-samples/bolt-python-ai-chatbot/blob/main/manifest.json) file into the text box that says **Paste your manifest code here** (within the **JSON** tab) and click **Next**. 4. Review the configuration and click **Create**. -5. You're now in your app configuration's **Basic Information** page. Navigate to the **Install App** link in the left nav and click **Install to Workspace*, then **Allow** on the screen that follows. +5. You're now in your app configuration's **Basic Information** page. Navigate to the **Install App** link in the left nav and click **Install to Workspace**, then **Allow** on the screen that follows. ### Obtaining and storing your environment variables {#environment-variables} Before you'll be able to successfully run the app, you'll need to first obtain and set some environment variables. +#### Slack tokens {#slack-tokens} + +From your app's page on [app settings](https://api.slack.com/apps) collect an app and bot token: + 1. On the **Install App** page, copy your **Bot User OAuth Token**. You will store this in your environment as `SLACK_BOT_TOKEN` (we'll get to that next). 2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](/reference/scopes/connections.write) scope, name the token, and click **Generate**. (For more details, refer to [understanding OAuth scopes for bots](/authentication/tokens#bot)). Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`. -To store your tokens and environment variables, run the following commands in the terminal. Replace the placeholder values with your bot and app tokens collected above, as well as the key or keys for the AI provider or providers you want to use: +To store your tokens and environment variables, run the following commands in the terminal. Replace the placeholder values with your bot and app tokens collected above: **For macOS** + ```bash export SLACK_BOT_TOKEN= export SLACK_APP_TOKEN= -export OPENAI_API_KEY= -export ANTHROPIC_API_KEY= ``` **For Windows** -```bash + +```pwsh set SLACK_BOT_TOKEN= set SLACK_APP_TOKEN= -set OPENAI_API_KEY= -set ANTHROPIC_API_KEY= +``` + +#### Provider tokens {#provider-tokens} + +Models from different AI providers are available if the corresponding environment variable is added as shown in the sections below. + +##### Anthropic {#anthropic} + +To interact with Anthropic models, navigate to your Anthropic account dashboard to [create an API key](https://console.anthropic.com/settings/keys), then export the key as follows: + +```bash +export ANTHROPIC_API_KEY= +``` + +##### Google Cloud Vertex AI {#google-cloud-vertex-ai} + +To use Google Cloud Vertex AI, [follow this quick start](https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal#expandable-1) to create a project for sending requests to the Gemini API, then gather [Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc) with the strategy to match your development environment. + +Once your project and credentials are configured, export environment variables to select from Gemini models: + +```bash +export VERTEX_AI_PROJECT_ID= +export VERTEX_AI_LOCATION= +``` + +The project location can be located under the **Region** on the [Vertex AI](https://console.cloud.google.com/vertex-ai) dashboard, as well as more details about available Gemini models. + +##### OpenAI {#openai} + +Unlock the OpenAI models from your OpenAI account dashboard by clicking [create a new secret key](https://platform.openai.com/api-keys), then export the key like so: + +```bash +export OPENAI_API_KEY= ``` ## Setting up and running your local project {#configure-project} @@ -69,12 +104,14 @@ cd bolt-python-ai-chatbot Start your Python virtual environment: **For macOS** + ```bash python3 -m venv .venv source .venv/bin/activate ``` **For Windows** + ```bash py -m venv .venv .venv\Scripts\activate @@ -123,7 +160,7 @@ Under **Then, do these things**, click **Add steps** and complete the following: ![Send a message](3.png) -We'll add two more steps under the **Then, do these things** section. +We'll add two more steps under the **Then, do these things** section. First, scroll to the bottom of the list of steps and choose **Custom**, then choose **Bolty** and **Bolty Custom Function**. In the **Channel** drop-down menu, select **Channel that the user joined**. Click **Save**. @@ -142,7 +179,7 @@ When finished, click **Finish Up**, then click **Publish** to make the workflow ### Summarizing recent conversations {#summarize} -In order for Bolty to provide summaries of recent conversation in a channel, Bolty _must_ be a member of that channel. +In order for Bolty to provide summaries of recent conversation in a channel, Bolty _must_ be a member of that channel. 1. Invite Bolty to a channel that you are able to leave and rejoin (for example, not the **#general** channel or a private channel someone else created) by mentioning the app in the channel — i.e., tagging **@Bolty** in the channel and sending your message. 2. Slackbot will prompt you to either invite Bolty to the channel, or do nothing. Click **Invite Them**. Now when new users join the channel, the workflow you just created will be kicked off. @@ -189,7 +226,7 @@ def handle_summary_function_callback( To ask Bolty a question, you can chat with Bolty in any channel the app is in. Use the `\ask-bolty` slash command to provide a prompt for Bolty to answer. Note that Bolty is currently not supported in threads. -You can also navigate to **Bolty** in your **Apps** list and select the **Messages** tab to chat with Bolty directly. +You can also navigate to **Bolty** in your **Apps** list and select the **Messages** tab to chat with Bolty directly. ![Ask Bolty](8.png) @@ -197,6 +234,6 @@ You can also navigate to **Bolty** in your **Apps** list and select the **Messag Congratulations! You've successfully integrated the power of AI into your workspace. Check out these links to take the next steps in your Bolt for Python journey. -* To learn more about Bolt for Python, refer to the [Getting started](/tools/bolt-python/getting-started) documentation. -* For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. -* To use the Bolt for Python SDK to develop on the automations platform, refer to the [Create a workflow step for Workflow Builder: Bolt for Python](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) tutorial. +- To learn more about Bolt for Python, refer to the [Getting started](/tools/bolt-python/getting-started) documentation. +- For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. +- To use the Bolt for Python SDK to develop on the automations platform, refer to the [Create a workflow step for Workflow Builder: Bolt for Python](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) tutorial. From 811d6a25466f7eb119331b47eb94f13402d34ab9 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 25 Sep 2025 16:53:25 -0700 Subject: [PATCH 147/282] build: require cheroot<11 with adapter test dependencies (#1375) --- requirements/adapter.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 8fd5cd33e..3cefd621d 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -4,6 +4,7 @@ boto3<=2 bottle>=0.12,<1 chalice>=1.28,<2; +cheroot<11 # https://github.com/slackapi/bolt-python/issues/1374 CherryPy>=18,<19 Django>=3,<6 falcon>=2,<5; python_version<"3.11" From e21c4e82800ddff12e8933b0f4ba9ff19e3202fd Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 25 Sep 2025 17:57:42 -0700 Subject: [PATCH 148/282] build(deps): remove pytest lower bounds from testing requirements (#1333) Co-authored-by: William Bergamin --- requirements/testing_without_asyncio.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/testing_without_asyncio.txt b/requirements/testing_without_asyncio.txt index 0e493f0e2..441b49f8b 100644 --- a/requirements/testing_without_asyncio.txt +++ b/requirements/testing_without_asyncio.txt @@ -1,3 +1,3 @@ # pip install -r requirements/testing_without_asyncio.txt -pytest>=6.2.5,<8.5 # https://github.com/tornadoweb/tornado/issues/3375 +pytest<8.5 pytest-cov>=3,<8 From ef3e178b950cd4a22abf1ed59fe50152e9b0138e Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:39:09 -0500 Subject: [PATCH 149/282] docs: updates for combined quickstart (#1378) --- docs/english/getting-started.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/english/getting-started.md b/docs/english/getting-started.md index a198736bc..8b7438d65 100644 --- a/docs/english/getting-started.md +++ b/docs/english/getting-started.md @@ -1,9 +1,8 @@ --- sidebar_label: Quickstart +title: Quickstart guide with Bolt for Python --- -# Quickstart guide with Bolt for Python - This quickstart guide aims to help you get a Slack app using Bolt for Python up and running as soon as possible! import Tabs from '@theme/Tabs'; @@ -292,8 +291,8 @@ Follow along with the steps that went into making this app on the [building an a You can now continue customizing your app with various features to make it right for whatever job's at hand. Here are some ideas about what to explore next: -- Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. All of the [events](/reference/events) are listed on the API docs site. -- Bolt allows you to call [Web API](/tools/bolt-python/concepts/web-api) methods with the client attached to your app. There are [over 200 methods](/reference/methods) on the API docs site. +- Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. See the full events reference [here](/reference/events). +- Bolt allows you to call [Web API](/tools/bolt-python/concepts/web-api) methods with the client attached to your app. There are [over 200 methods](/reference/methods) available. - Learn more about the different [token types](/authentication/tokens) and [authentication setups](/tools/bolt-python/concepts/authenticating-oauth). Your app might need different tokens depending on the actions you want to perform or for installations to multiple workspaces. - Receive events using HTTP for various deployment methods, such as deploying to Heroku or AWS Lambda. - Read on [app design](/surfaces/app-design) and compose fancy messages with blocks using [Block Kit Builder](https://app.slack.com/block-kit-builder) to prototype messages. From 9fa6e86fa345c243d2a604c5ef91f225e0448064 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 30 Sep 2025 11:49:12 -0700 Subject: [PATCH 150/282] build: install dependencies needed to autogenerate reference docs (#1377) --- scripts/generate_api_docs.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/generate_api_docs.sh b/scripts/generate_api_docs.sh index f8ea39d0d..c3b9fd260 100755 --- a/scripts/generate_api_docs.sh +++ b/scripts/generate_api_docs.sh @@ -1,10 +1,15 @@ #!/bin/bash # Generate API documents from the latest source code -script_dir=`dirname $0` -cd ${script_dir}/.. +set -e +script_dir=$(dirname "$0") +cd "${script_dir}/.." +pip install -U pip +pip install -U -r requirements/adapter.txt +pip install -U -r requirements/async.txt pip install -U pdoc3 +pip install . rm -rf docs/reference pdoc slack_bolt --html -o docs/reference From cb4130adf305a1299d9864d227e333fddb1739e6 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 30 Sep 2025 13:27:32 -0700 Subject: [PATCH 151/282] ci: post regression notifications if scheduled tests do not succeed (#1376) --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e68997aef..167dc2ce4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -86,7 +86,7 @@ jobs: name: Regression notifications runs-on: ubuntu-latest needs: build - if: failure() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' + if: ${{ !success() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' }} steps: - name: Send notifications of failing tests uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1 From c3e26d94b529278f5107e8fde1b1f011b1727cf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:35:16 -0700 Subject: [PATCH 152/282] chore(deps): bump mypy from 1.17.1 to 1.18.2 (#1379) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index b4cf790b9..73a342ca6 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.17.1 +mypy==1.18.2 flake8==7.3.0 black==25.1.0 From 95150b80e2ebe688fd834d6ceeaa3468334b25ae Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 3 Oct 2025 10:23:07 -0700 Subject: [PATCH 153/282] docs: replace links from api.slack.com to docs.slack.dev redirects (#1383) --- .github/ISSUE_TEMPLATE/03_document.md | 2 +- README.md | 12 +- .../custom-steps-workflow-builder-existing.md | 2 +- docs/reference/app/app.html | 132 ++++++++--------- docs/reference/app/async_app.html | 126 ++++++++-------- docs/reference/app/index.html | 132 ++++++++--------- docs/reference/async_app.html | 126 ++++++++-------- docs/reference/authorization/index.html | 2 +- .../thread_context_store/file/index.html | 2 +- docs/reference/context/index.html | 2 +- docs/reference/index.html | 138 +++++++++--------- docs/reference/lazy_listener/index.html | 2 +- docs/reference/listener_matcher/builtins.html | 2 +- docs/reference/logger/messages.html | 2 +- docs/reference/middleware/async_builtins.html | 12 +- docs/reference/middleware/index.html | 16 +- .../async_request_verification.html | 6 +- .../request_verification/index.html | 4 +- .../request_verification.html | 4 +- .../middleware/ssl_check/async_ssl_check.html | 4 +- .../reference/middleware/ssl_check/index.html | 8 +- .../middleware/ssl_check/ssl_check.html | 8 +- .../async_url_verification.html | 2 +- .../middleware/url_verification/index.html | 4 +- .../url_verification/url_verification.html | 4 +- docs/reference/oauth/index.html | 2 +- docs/reference/request/index.html | 2 +- docs/reference/response/index.html | 2 +- docs/reference/workflows/index.html | 2 +- docs/reference/workflows/step/async_step.html | 40 ++--- docs/reference/workflows/step/index.html | 12 +- docs/reference/workflows/step/step.html | 40 ++--- .../step/utilities/async_configure.html | 4 +- .../workflows/step/utilities/configure.html | 4 +- examples/aws_lambda/README.md | 14 +- examples/django/README.md | 8 +- examples/getting_started/README.md | 8 +- examples/message_events.py | 6 +- examples/readme_app.py | 4 +- examples/readme_async_app.py | 4 +- .../workflow_steps/async_steps_from_apps.py | 2 +- .../async_steps_from_apps_decorator.py | 2 +- .../async_steps_from_apps_primitive.py | 2 +- examples/workflow_steps/steps_from_apps.py | 2 +- .../steps_from_apps_decorator.py | 2 +- .../steps_from_apps_primitive.py | 2 +- pyproject.toml | 2 +- slack_bolt/__init__.py | 4 +- slack_bolt/app/app.py | 48 +++--- slack_bolt/app/async_app.py | 46 +++--- slack_bolt/authorization/__init__.py | 2 +- slack_bolt/context/__init__.py | 2 +- slack_bolt/lazy_listener/__init__.py | 2 +- slack_bolt/listener_matcher/builtins.py | 2 +- slack_bolt/logger/messages.py | 2 +- .../async_request_verification.py | 2 +- .../request_verification.py | 2 +- slack_bolt/middleware/ssl_check/ssl_check.py | 4 +- .../url_verification/url_verification.py | 2 +- slack_bolt/oauth/__init__.py | 2 +- slack_bolt/request/__init__.py | 2 +- slack_bolt/response/__init__.py | 2 +- slack_bolt/workflows/__init__.py | 2 +- slack_bolt/workflows/step/async_step.py | 16 +- slack_bolt/workflows/step/step.py | 16 +- .../step/utilities/async_configure.py | 2 +- .../workflows/step/utilities/configure.py | 2 +- .../scenario_tests/test_attachment_actions.py | 2 +- .../test_attachment_actions.py | 2 +- 69 files changed, 545 insertions(+), 541 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/03_document.md b/.github/ISSUE_TEMPLATE/03_document.md index 3ce4e48e5..4eb5af847 100644 --- a/.github/ISSUE_TEMPLATE/03_document.md +++ b/.github/ISSUE_TEMPLATE/03_document.md @@ -10,7 +10,7 @@ assignees: '' ### The page URLs -* https://slack.dev/bolt-python/ +* https://docs.slack.dev/tools/bolt-python/ ## Requirements diff --git a/README.md b/README.md index b3f78adb0..39747df40 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ ngrok http 3000 ## Running a Socket Mode app -If you use [Socket Mode](https://api.slack.com/socket-mode) for running your app, `SocketModeHandler` is available for it. +If you use [Socket Mode](https://docs.slack.dev/apis/events-api/using-socket-mode/) for running your app, `SocketModeHandler` is available for it. ```python import os @@ -91,7 +91,7 @@ python app.py ## Listening for events -Apps typically react to a collection of incoming events, which can correspond to [Events API events](https://api.slack.com/events-api), [actions](https://api.slack.com/interactivity/components), [shortcuts](https://api.slack.com/interactivity/shortcuts), [slash commands](https://api.slack.com/interactivity/slash-commands) or [options requests](https://api.slack.com/reference/block-kit/block-elements#external_select). For each type of +Apps typically react to a collection of incoming events, which can correspond to [Events API events](https://docs.slack.dev/apis/events-api/), [actions](https://docs.slack.dev/block-kit/#making-things-interactive), [shortcuts](https://docs.slack.dev/interactivity/implementing-shortcuts/), [slash commands](https://docs.slack.dev/interactivity/implementing-slash-commands/) or [options requests](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select). For each type of request, there's a method to build a listener function. ```python @@ -138,12 +138,12 @@ Most of the app's functionality will be inside listener functions (the `fn` para | Argument | Description | | :---: | :--- | | `body` | Dictionary that contains the entire body of the request (superset of `payload`). Some accessory data is only available outside of the payload (such as `trigger_id` and `authorizations`). -| `payload` | Contents of the incoming event. The payload structure depends on the listener. For example, for an Events API event, `payload` will be the [event type structure](https://api.slack.com/events-api#event_type_structure). For a block action, it will be the action from within the `actions` list. The `payload` dictionary is also accessible via the alias corresponding to the listener (`message`, `event`, `action`, `shortcut`, `view`, `command`, or `options`). For example, if you were building a `message()` listener, you could use the `payload` and `message` arguments interchangably. **An easy way to understand what's in a payload is to log it**. | +| `payload` | Contents of the incoming event. The payload structure depends on the listener. For example, for an Events API event, `payload` will be the [event type structure](https://docs.slack.dev/apis/events-api/#event-type-structure). For a block action, it will be the action from within the `actions` list. The `payload` dictionary is also accessible via the alias corresponding to the listener (`message`, `event`, `action`, `shortcut`, `view`, `command`, or `options`). For example, if you were building a `message()` listener, you could use the `payload` and `message` arguments interchangably. **An easy way to understand what's in a payload is to log it**. | | `context` | Event context. This dictionary contains data about the event and app, such as the `botId`. Middleware can add additional context before the event is passed to listeners. -| `ack` | Function that **must** be called to acknowledge that your app received the incoming event. `ack` exists for all actions, shortcuts, view submissions, slash command and options requests. `ack` returns a promise that resolves when complete. Read more in [Acknowledging events](https://tools.slack.dev/bolt-python/concepts/acknowledge). +| `ack` | Function that **must** be called to acknowledge that your app received the incoming event. `ack` exists for all actions, shortcuts, view submissions, slash command and options requests. `ack` returns a promise that resolves when complete. Read more in [Acknowledging events](https://docs.slack.dev/tools/bolt-python/concepts/acknowledge/). | `respond` | Utility function that responds to incoming events **if** it contains a `response_url` (shortcuts, actions, and slash commands). | `say` | Utility function to send a message to the channel associated with the incoming event. This argument is only available when the listener is triggered for events that contain a `channel_id` (the most common being `message` events). `say` accepts simple strings (for plain-text messages) and dictionaries (for messages containing blocks). -| `client` | Web API client that uses the token associated with the event. For single-workspace installations, the token is provided to the constructor. For multi-workspace installations, the token is returned by using [the OAuth library](https://tools.slack.dev/bolt-python/concepts/authenticating-oauth), or manually using the `authorize` function. +| `client` | Web API client that uses the token associated with the event. For single-workspace installations, the token is provided to the constructor. For multi-workspace installations, the token is returned by using [the OAuth library](https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth/), or manually using the `authorize` function. | `logger` | The built-in [`logging.Logger`](https://docs.python.org/3/library/logging.html) instance you can use in middleware/listeners. | `complete` | Utility function used to signal the successful completion of a custom step execution. This tells Slack to proceed with the next steps in the workflow. This argument is only available with the `.function` and `.action` listener when handling custom workflow step executions. | `fail` | Utility function used to signal that a custom step failed to complete. This tells Slack to stop the workflow execution. This argument is only available with the `.function` and `.action` listener when handling custom workflow step executions. @@ -192,7 +192,7 @@ Apps can be run the same way as the syncronous example above. If you'd prefer an ## Getting Help -[The documentation](https://tools.slack.dev/bolt-python) has more information on basic and advanced concepts for Bolt for Python. Also, all the Python module documents of this library are available [here](https://tools.slack.dev/bolt-python/reference/). +[The documentation](https://docs.slack.dev/tools/bolt-python/) has more information on basic and advanced concepts for Bolt for Python. Also, all the Python module documents of this library are available [here](https://docs.slack.dev/tools/bolt-python/reference/). If you otherwise get stuck, we're here to help. The following are the best ways to get assistance working through your issue: diff --git a/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md b/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md index 0441b033c..c3c5e2af7 100644 --- a/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md +++ b/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md @@ -9,7 +9,7 @@ If you followed along with our [create a custom step for Workflow Builder: new a In this tutorial we will: - Start with an existing Bolt app - Add a custom **workflow step** in the [app settings](https://api.slack.com/apps) -- Wire up the new step to a **function listener** in our project, using the [Bolt for Python](https://slack.dev/bolt-python/) framework +- Wire up the new step to a **function listener** in our project, using the [Bolt for Python](https://docs.slack.dev/tools/bolt-python/) framework - See the step as a custom workflow step in Workflow Builder ## Prerequisites {#prereqs} diff --git a/docs/reference/app/app.html b/docs/reference/app/app.html index d1224dd5d..3ee02b07c 100644 --- a/docs/reference/app/app.html +++ b/docs/reference/app/app.html @@ -117,10 +117,10 @@

    Classes

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://slack.dev/bolt-python/tutorial/getting-started for details. + Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, - refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app. + refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. @@ -629,7 +629,7 @@

    Classes

    # Pass a function to this method app.middleware(middleware_func) - Refer to https://slack.dev/bolt-python/concepts#global-middleware for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -675,7 +675,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -693,7 +693,7 @@

    Classes

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -710,7 +710,7 @@

    Classes

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -787,7 +787,7 @@

    Classes

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -825,7 +825,7 @@

    Classes

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -936,7 +936,7 @@

    Classes

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -983,7 +983,7 @@

    Classes

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1051,9 +1051,9 @@

    Classes

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1079,7 +1079,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1096,7 +1096,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1112,7 +1112,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1128,7 +1128,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1169,7 +1169,7 @@

    Classes

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1195,7 +1195,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1211,7 +1211,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1252,8 +1252,8 @@

    Classes

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1293,7 +1293,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1443,9 +1443,9 @@

    Classes

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000)))
    -

    Refer to https://slack.dev/bolt-python/tutorial/getting-started for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details.

    If you would like to build an OAuth app for enabling the app to run with multiple workspaces, -refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app.

    +refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

    Args

    logger
    @@ -1637,9 +1637,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1668,9 +1668,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1713,7 +1713,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1723,7 +1723,7 @@

    Args

    return __call__

    Registers a new interactive_message action listener. -Refer to https://api.slack.com/legacy/message-buttons for details.

    +Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1740,7 +1740,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1751,7 +1751,7 @@

    Args

    return __call__

    Registers a new block_actions action listener. -Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1805,7 +1805,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1836,7 +1836,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1899,7 +1899,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1909,7 +1909,7 @@

    Args

    return __call__

    Registers a new dialog_cancellation listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1926,7 +1926,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1936,7 +1936,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1953,7 +1953,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1963,7 +1963,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dispatch(self,
    req: BoltRequest) ‑> BoltResponse
    @@ -2189,7 +2189,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2221,7 +2221,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    +

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2373,7 +2373,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2424,7 +2424,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello)
    -

    Refer to https://api.slack.com/events/message for details of message events.

    +

    Refer to https://docs.slack.dev/reference/events/message/ for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2484,7 +2484,7 @@

    Args

    # Pass a function to this method app.middleware(middleware_func) - Refer to https://slack.dev/bolt-python/concepts#global-middleware for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2522,7 +2522,7 @@

    Args

    # Pass a function to this method app.middleware(middleware_func)
    -

    Refer to https://slack.dev/bolt-python/concepts#global-middleware for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2567,8 +2567,8 @@

    Args

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2608,8 +2608,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2655,7 +2655,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2692,7 +2692,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2778,7 +2778,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -2796,7 +2796,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2813,7 +2813,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -2835,7 +2835,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use WorkflowStepBuilder's methods.

    @@ -2850,7 +2850,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    For further information about WorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -2921,7 +2921,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2962,7 +2962,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    +

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2991,7 +2991,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3001,7 +3001,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -3018,7 +3018,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3028,7 +3028,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    diff --git a/docs/reference/app/async_app.html b/docs/reference/app/async_app.html index b6634710a..78959986b 100644 --- a/docs/reference/app/async_app.html +++ b/docs/reference/app/async_app.html @@ -114,10 +114,10 @@

    Classes

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://slack.dev/bolt-python/concepts#async for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/async for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, - refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app. + refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. @@ -687,7 +687,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -705,7 +705,7 @@

    Classes

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -721,7 +721,7 @@

    Classes

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -803,7 +803,7 @@

    Classes

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -841,7 +841,7 @@

    Classes

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -956,7 +956,7 @@

    Classes

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1003,7 +1003,7 @@

    Classes

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1071,9 +1071,9 @@

    Classes

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1099,7 +1099,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1116,7 +1116,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1132,7 +1132,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1148,7 +1148,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1189,7 +1189,7 @@

    Classes

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1215,7 +1215,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1231,7 +1231,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1272,8 +1272,8 @@

    Classes

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1313,7 +1313,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1473,9 +1473,9 @@

    Classes

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) -

    Refer to https://slack.dev/bolt-python/concepts#async for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/async for details.

    If you would like to build an OAuth app for enabling the app to run with multiple workspaces, -refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app.

    +refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

    Args

    logger
    @@ -1669,9 +1669,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1700,9 +1700,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -1873,7 +1873,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1883,7 +1883,7 @@

    Returns

    return __call__

    Registers a new interactive_message action listener. -Refer to https://api.slack.com/legacy/message-buttons for details.

    +Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -1900,7 +1900,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1911,7 +1911,7 @@

    Returns

    return __call__

    Registers a new block_actions action listener. -Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -1965,7 +1965,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1996,7 +1996,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2059,7 +2059,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2069,7 +2069,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2086,7 +2086,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2096,7 +2096,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2113,7 +2113,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2123,7 +2123,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def enable_token_revocation_listeners(self) ‑> None @@ -2229,7 +2229,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2261,7 +2261,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    +

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2414,7 +2414,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2468,7 +2468,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://api.slack.com/events/message for details of message events.

    +

    Refer to https://docs.slack.dev/reference/events/message/ for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2608,8 +2608,8 @@

    Args

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2649,8 +2649,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2739,7 +2739,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2776,7 +2776,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2839,7 +2839,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -2857,7 +2857,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -2873,7 +2873,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -2895,7 +2895,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use AsyncWorkflowStepBuilder's methods.

    @@ -2910,7 +2910,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document. For further information about AsyncWorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -2978,7 +2978,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -3019,7 +3019,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    +

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -3048,7 +3048,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3058,7 +3058,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -3075,7 +3075,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3085,7 +3085,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    def web_app(self, path: str = '/slack/events', port: int = 3000) ‑> aiohttp.web_app.Application diff --git a/docs/reference/app/index.html b/docs/reference/app/index.html index 857fb22c8..a46bc2e71 100644 --- a/docs/reference/app/index.html +++ b/docs/reference/app/index.html @@ -136,10 +136,10 @@

    Classes

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://slack.dev/bolt-python/tutorial/getting-started for details. + Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, - refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app. + refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. @@ -648,7 +648,7 @@

    Classes

    # Pass a function to this method app.middleware(middleware_func) - Refer to https://slack.dev/bolt-python/concepts#global-middleware for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -694,7 +694,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -712,7 +712,7 @@

    Classes

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -729,7 +729,7 @@

    Classes

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -806,7 +806,7 @@

    Classes

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -844,7 +844,7 @@

    Classes

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -955,7 +955,7 @@

    Classes

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1002,7 +1002,7 @@

    Classes

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1070,9 +1070,9 @@

    Classes

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1098,7 +1098,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1115,7 +1115,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1131,7 +1131,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1147,7 +1147,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1188,7 +1188,7 @@

    Classes

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1214,7 +1214,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1230,7 +1230,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1271,8 +1271,8 @@

    Classes

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1312,7 +1312,7 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1462,9 +1462,9 @@

    Classes

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000)))
    -

    Refer to https://slack.dev/bolt-python/tutorial/getting-started for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details.

    If you would like to build an OAuth app for enabling the app to run with multiple workspaces, -refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app.

    +refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

    Args

    logger
    @@ -1656,9 +1656,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1687,9 +1687,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1732,7 +1732,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1742,7 +1742,7 @@

    Args

    return __call__

    Registers a new interactive_message action listener. -Refer to https://api.slack.com/legacy/message-buttons for details.

    +Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1759,7 +1759,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1770,7 +1770,7 @@

    Args

    return __call__

    Registers a new block_actions action listener. -Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1824,7 +1824,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1855,7 +1855,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1918,7 +1918,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1928,7 +1928,7 @@

    Args

    return __call__

    Registers a new dialog_cancellation listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1945,7 +1945,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1955,7 +1955,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1972,7 +1972,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1982,7 +1982,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dispatch(self,
    req: BoltRequest) ‑> BoltResponse
    @@ -2208,7 +2208,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2240,7 +2240,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    +

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2392,7 +2392,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2443,7 +2443,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://api.slack.com/events/message for details of message events.

    +

    Refer to https://docs.slack.dev/reference/events/message/ for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2503,7 +2503,7 @@

    Args

    # Pass a function to this method app.middleware(middleware_func) - Refer to https://slack.dev/bolt-python/concepts#global-middleware for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2541,7 +2541,7 @@

    Args

    # Pass a function to this method app.middleware(middleware_func) -

    Refer to https://slack.dev/bolt-python/concepts#global-middleware for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2586,8 +2586,8 @@

    Args

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2627,8 +2627,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2674,7 +2674,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2711,7 +2711,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2797,7 +2797,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -2815,7 +2815,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2832,7 +2832,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -2854,7 +2854,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use WorkflowStepBuilder's methods.

    @@ -2869,7 +2869,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    For further information about WorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -2940,7 +2940,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2981,7 +2981,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    +

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -3010,7 +3010,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3020,7 +3020,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -3037,7 +3037,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3047,7 +3047,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    diff --git a/docs/reference/async_app.html b/docs/reference/async_app.html index ad9192253..707bfc3dd 100644 --- a/docs/reference/async_app.html +++ b/docs/reference/async_app.html @@ -205,10 +205,10 @@

    Class variables

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://slack.dev/bolt-python/concepts#async for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/async for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, - refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app. + refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. @@ -778,7 +778,7 @@

    Class variables

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -796,7 +796,7 @@

    Class variables

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -812,7 +812,7 @@

    Class variables

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -894,7 +894,7 @@

    Class variables

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -932,7 +932,7 @@

    Class variables

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1047,7 +1047,7 @@

    Class variables

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1094,7 +1094,7 @@

    Class variables

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1162,9 +1162,9 @@

    Class variables

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1190,7 +1190,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1207,7 +1207,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1223,7 +1223,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1239,7 +1239,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1280,7 +1280,7 @@

    Class variables

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1306,7 +1306,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1322,7 +1322,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1363,8 +1363,8 @@

    Class variables

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1404,7 +1404,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1564,9 +1564,9 @@

    Class variables

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) -

    Refer to https://slack.dev/bolt-python/concepts#async for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/async for details.

    If you would like to build an OAuth app for enabling the app to run with multiple workspaces, -refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app.

    +refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

    Args

    logger
    @@ -1760,9 +1760,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1791,9 +1791,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -1964,7 +1964,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1974,7 +1974,7 @@

    Returns

    return __call__

    Registers a new interactive_message action listener. -Refer to https://api.slack.com/legacy/message-buttons for details.

    +Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -1991,7 +1991,7 @@

    Returns

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -2002,7 +2002,7 @@

    Returns

    return __call__

    Registers a new block_actions action listener. -Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2056,7 +2056,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2087,7 +2087,7 @@

    Returns

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2150,7 +2150,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2160,7 +2160,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2177,7 +2177,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2187,7 +2187,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -2204,7 +2204,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2214,7 +2214,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def enable_token_revocation_listeners(self) ‑> None @@ -2320,7 +2320,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2352,7 +2352,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    +

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2505,7 +2505,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2559,7 +2559,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://api.slack.com/events/message for details of message events.

    +

    Refer to https://docs.slack.dev/reference/events/message/ for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2699,8 +2699,8 @@

    Args

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2740,8 +2740,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2830,7 +2830,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -2867,7 +2867,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -2930,7 +2930,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -2948,7 +2948,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -2964,7 +2964,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -2986,7 +2986,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use AsyncWorkflowStepBuilder's methods.

    @@ -3001,7 +3001,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document. For further information about AsyncWorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -3069,7 +3069,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -3110,7 +3110,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    +

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.async_args's API document.

    Args

    @@ -3139,7 +3139,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3149,7 +3149,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., Awaitable[bool]]] | None = None,
    middleware: Sequence[Callable | AsyncMiddleware] | None = None) ‑> Callable[..., Callable[..., Awaitable[BoltResponse | None]] | None]
    @@ -3166,7 +3166,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3176,7 +3176,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    def web_app(self, path: str = '/slack/events', port: int = 3000) ‑> aiohttp.web_app.Application diff --git a/docs/reference/authorization/index.html b/docs/reference/authorization/index.html index 64ca14f0e..19de311df 100644 --- a/docs/reference/authorization/index.html +++ b/docs/reference/authorization/index.html @@ -39,7 +39,7 @@

    Module slack_bolt.authorization

    Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack event.

    -

    Refer to https://slack.dev/bolt-python/concepts#authorization for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/authorization for details.

    Sub-modules

    diff --git a/docs/reference/context/assistant/thread_context_store/file/index.html b/docs/reference/context/assistant/thread_context_store/file/index.html index 4a5d944e1..cbb4e4db6 100644 --- a/docs/reference/context/assistant/thread_context_store/file/index.html +++ b/docs/reference/context/assistant/thread_context_store/file/index.html @@ -48,7 +48,7 @@

    Classes

    class FileAssistantThreadContextStore -(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts')
    diff --git a/docs/reference/context/index.html b/docs/reference/context/index.html index 65cb8054c..c761aa47e 100644 --- a/docs/reference/context/index.html +++ b/docs/reference/context/index.html @@ -40,7 +40,7 @@

    Module slack_bolt.context

    All listeners have access to a context dictionary, which can be used to enrich events with additional information. Bolt automatically attaches information that is included in the incoming event, like user_id, team_id, channel_id, and enterprise_id.

    -

    Refer to https://slack.dev/bolt-python/concepts#context for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/context for details.

    Sub-modules

    diff --git a/docs/reference/index.html b/docs/reference/index.html index 430e36813..1ce8cd134 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -36,9 +36,9 @@

    Package slack_bolt

    -

    A Python framework to build Slack apps in a flash with the latest platform features.Read the getting started guide and look at our code examples to learn how to build apps using Bolt.

    +

    A Python framework to build Slack apps in a flash with the latest platform features.Read the getting started guide and look at our code examples to learn how to build apps using Bolt.

    @@ -257,10 +257,10 @@

    Class variables

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://slack.dev/bolt-python/tutorial/getting-started for details. + Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, - refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app. + refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. @@ -769,7 +769,7 @@

    Class variables

    # Pass a function to this method app.middleware(middleware_func) - Refer to https://slack.dev/bolt-python/concepts#global-middleware for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -815,7 +815,7 @@

    Class variables

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -833,7 +833,7 @@

    Class variables

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -850,7 +850,7 @@

    Class variables

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -927,7 +927,7 @@

    Class variables

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -965,7 +965,7 @@

    Class variables

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1076,7 +1076,7 @@

    Class variables

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1123,7 +1123,7 @@

    Class variables

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1191,9 +1191,9 @@

    Class variables

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1219,7 +1219,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1236,7 +1236,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1252,7 +1252,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1268,7 +1268,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1309,7 +1309,7 @@

    Class variables

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1335,7 +1335,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1351,7 +1351,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1392,8 +1392,8 @@

    Class variables

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1433,7 +1433,7 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1583,9 +1583,9 @@

    Class variables

    if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000)))
    -

    Refer to https://slack.dev/bolt-python/tutorial/getting-started for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details.

    If you would like to build an OAuth app for enabling the app to run with multiple workspaces, -refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app.

    +refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

    Args

    logger
    @@ -1777,9 +1777,9 @@

    Methods

    # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1808,9 +1808,9 @@

    Methods

    app.action("approve_button")(update_message)

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -1853,7 +1853,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1863,7 +1863,7 @@

    Args

    return __call__

    Registers a new interactive_message action listener. -Refer to https://api.slack.com/legacy/message-buttons for details.

    +Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.

    def block_action(self,
    constraints: str | Pattern | Dict[str, str | Pattern],
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1880,7 +1880,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1891,7 +1891,7 @@

    Args

    return __call__

    Registers a new block_actions action listener. -Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details.

    def block_suggestion(self,
    action_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -1945,7 +1945,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1976,7 +1976,7 @@

    Args

    # Pass a function to this method app.command("/echo")(repeat_text)
    -

    Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2039,7 +2039,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2049,7 +2049,7 @@

    Args

    return __call__

    Registers a new dialog_cancellation listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_submission(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -2066,7 +2066,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2076,7 +2076,7 @@

    Args

    return __call__

    Registers a new dialog_submission listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dialog_suggestion(self,
    callback_id: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -2093,7 +2093,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -2103,7 +2103,7 @@

    Args

    return __call__

    Registers a new dialog_suggestion listener. -Refer to https://api.slack.com/dialogs for details.

    +Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.

    def dispatch(self,
    req: BoltRequest) ‑> BoltResponse
    @@ -2329,7 +2329,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2361,7 +2361,7 @@

    Args

    # Pass a function to this method app.event("team_join")(ask_for_introduction)
    -

    Refer to https://api.slack.com/apis/connections/events-api for details of Events API.

    +

    Refer to https://docs.slack.dev/apis/events-api/ for details of Events API.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2513,7 +2513,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2564,7 +2564,7 @@

    Args

    # Pass a function to this method app.message(":wave:")(say_hello) -

    Refer to https://api.slack.com/events/message for details of message events.

    +

    Refer to https://docs.slack.dev/reference/events/message/ for details of message events.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2624,7 +2624,7 @@

    Args

    # Pass a function to this method app.middleware(middleware_func) - Refer to https://slack.dev/bolt-python/concepts#global-middleware for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2662,7 +2662,7 @@

    Args

    # Pass a function to this method app.middleware(middleware_func) -

    Refer to https://slack.dev/bolt-python/concepts#global-middleware for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2707,8 +2707,8 @@

    Args

    Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2748,8 +2748,8 @@

    Args

    Refer to the following documents for details:

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2795,7 +2795,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2832,7 +2832,7 @@

    Args

    # Pass a function to this method app.shortcut("open_modal")(open_modal) -

    Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts.

    +

    Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -2918,7 +2918,7 @@

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -2936,7 +2936,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -2953,7 +2953,7 @@

    Args

    warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -2975,7 +2975,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new step from app listener.

    Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use WorkflowStepBuilder's methods.

    @@ -2990,7 +2990,7 @@

    Args

    # Pass Step to set up listeners app.step(ws) -

    Refer to https://api.slack.com/workflows/steps for details of steps from apps.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    For further information about WorkflowStep specific function arguments such as configure, update, complete, and fail, @@ -3061,7 +3061,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -3102,7 +3102,7 @@

    Args

    # Pass a function to this method app.view("view_1")(handle_submission) -

    Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads.

    +

    Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads.

    To learn available arguments for middleware/listeners, see slack_bolt.kwargs_injection.args's API document.

    Args

    @@ -3131,7 +3131,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3141,7 +3141,7 @@

    Args

    return __call__

    Registers a new view_closed listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.

    def view_submission(self,
    constraints: str | Pattern,
    matchers: Sequence[Callable[..., bool]] | None = None,
    middleware: Sequence[Callable | Middleware] | None = None) ‑> Callable[..., Callable[..., BoltResponse | None] | None]
    @@ -3158,7 +3158,7 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3168,7 +3168,7 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    @@ -5225,7 +5225,7 @@

    Class variables

    class FileAssistantThreadContextStore -(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts')
    diff --git a/docs/reference/lazy_listener/index.html b/docs/reference/lazy_listener/index.html index c2eb1c9b0..6bc17015e 100644 --- a/docs/reference/lazy_listener/index.html +++ b/docs/reference/lazy_listener/index.html @@ -56,7 +56,7 @@

    Module slack_bolt.lazy_listener

    lazy=[run_long_process] ) -

    Refer to https://slack.dev/bolt-python/concepts#lazy-listeners for more details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/lazy-listeners for more details.

    Sub-modules

    diff --git a/docs/reference/listener_matcher/builtins.html b/docs/reference/listener_matcher/builtins.html index d951deada..a5aff3d0b 100644 --- a/docs/reference/listener_matcher/builtins.html +++ b/docs/reference/listener_matcher/builtins.html @@ -80,7 +80,7 @@

    Functions

    return dialog_submission(constraints["callback_id"], asyncio) if action_type == "dialog_cancellation": return dialog_cancellation(constraints["callback_id"], asyncio) - # https://api.slack.com/workflows/steps + # https://docs.slack.dev/legacy/legacy-steps-from-apps/ if action_type == "workflow_step_edit": return workflow_step_edit(constraints["callback_id"], asyncio) diff --git a/docs/reference/logger/messages.html b/docs/reference/logger/messages.html index 85e0d94dd..1072e6479 100644 --- a/docs/reference/logger/messages.html +++ b/docs/reference/logger/messages.html @@ -303,7 +303,7 @@

    Functions

    "Bolt has enabled the file-based InstallationStore/OAuthStateStore for you. " "Note that these file-based stores are for local development. " "If you'd like to use a different data store, set the oauth_settings argument in the App constructor. " - "Please refer to https://slack.dev/bolt-python/concepts#authenticating-oauth for more details." + "Please refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth for more details." )
    diff --git a/docs/reference/middleware/async_builtins.html b/docs/reference/middleware/async_builtins.html index 7528dc0bb..d32deff15 100644 --- a/docs/reference/middleware/async_builtins.html +++ b/docs/reference/middleware/async_builtins.html @@ -205,7 +205,7 @@

    Inherited members

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. + Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. """ async def async_process( @@ -232,10 +232,10 @@

    Inherited members

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details.

    Args

    signing_secret
    @@ -293,12 +293,12 @@

    Inherited members

    A middleware can process request data before other middleware and listener functions.

    Handles ssl_check requests. -Refer to https://api.slack.com/interactivity/slash-commands for details.

    +Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation)
    base_logger
    The base logger
    @@ -352,7 +352,7 @@

    Inherited members

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://api.slack.com/events/url_verification for details.

    +

    Refer to https://docs.slack.dev/reference/events/url_verification/ for details.

    Args

    base_logger
    diff --git a/docs/reference/middleware/index.html b/docs/reference/middleware/index.html index 98aa15c5d..05d773415 100644 --- a/docs/reference/middleware/index.html +++ b/docs/reference/middleware/index.html @@ -639,7 +639,7 @@

    Inherited members

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. + Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. Args: signing_secret: The signing secret @@ -688,7 +688,7 @@

    Inherited members

    A middleware can process request data before other middleware and listener functions.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details.

    Args

    signing_secret
    @@ -834,11 +834,11 @@

    Inherited members

    base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. - Refer to https://api.slack.com/interactivity/slash-commands for details. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details. Args: verification_token: The verification token to check - (optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation) + (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token @@ -880,12 +880,12 @@

    Inherited members

    A middleware can process request data before other middleware and listener functions.

    Handles slack_bolt.middleware.ssl_check requests. -Refer to https://api.slack.com/interactivity/slash-commands for details.

    +Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation)
    base_logger
    The base logger
    @@ -931,7 +931,7 @@

    Inherited members

    def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. - Refer to https://api.slack.com/events/url_verification for details. + Refer to https://docs.slack.dev/reference/events/url_verification/ for details. Args: base_logger: The base logger @@ -965,7 +965,7 @@

    Inherited members

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://api.slack.com/events/url_verification for details.

    +

    Refer to https://docs.slack.dev/reference/events/url_verification/ for details.

    Args

    base_logger
    diff --git a/docs/reference/middleware/request_verification/async_request_verification.html b/docs/reference/middleware/request_verification/async_request_verification.html index dc2b20908..192f77933 100644 --- a/docs/reference/middleware/request_verification/async_request_verification.html +++ b/docs/reference/middleware/request_verification/async_request_verification.html @@ -59,7 +59,7 @@

    Classes

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. + Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. """ async def async_process( @@ -86,10 +86,10 @@

    Classes

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details.

    Args

    signing_secret
    diff --git a/docs/reference/middleware/request_verification/index.html b/docs/reference/middleware/request_verification/index.html index ec8e6b941..5dfd6ed82 100644 --- a/docs/reference/middleware/request_verification/index.html +++ b/docs/reference/middleware/request_verification/index.html @@ -71,7 +71,7 @@

    Classes

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. + Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. Args: signing_secret: The signing secret @@ -120,7 +120,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details.

    Args

    signing_secret
    diff --git a/docs/reference/middleware/request_verification/request_verification.html b/docs/reference/middleware/request_verification/request_verification.html index aa5da095f..99134110a 100644 --- a/docs/reference/middleware/request_verification/request_verification.html +++ b/docs/reference/middleware/request_verification/request_verification.html @@ -60,7 +60,7 @@

    Classes

    """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. + Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. Args: signing_secret: The signing secret @@ -109,7 +109,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Verifies an incoming request by checking the validity of x-slack-signature, x-slack-request-timestamp, and its body data.

    -

    Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

    +

    Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details.

    Args

    signing_secret
    diff --git a/docs/reference/middleware/ssl_check/async_ssl_check.html b/docs/reference/middleware/ssl_check/async_ssl_check.html index eaacf0846..48c4bb599 100644 --- a/docs/reference/middleware/ssl_check/async_ssl_check.html +++ b/docs/reference/middleware/ssl_check/async_ssl_check.html @@ -75,12 +75,12 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles ssl_check requests. -Refer to https://api.slack.com/interactivity/slash-commands for details.

    +Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation)
    base_logger
    The base logger
    diff --git a/docs/reference/middleware/ssl_check/index.html b/docs/reference/middleware/ssl_check/index.html index 6a6477071..6c1e4725e 100644 --- a/docs/reference/middleware/ssl_check/index.html +++ b/docs/reference/middleware/ssl_check/index.html @@ -76,11 +76,11 @@

    Classes

    base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. - Refer to https://api.slack.com/interactivity/slash-commands for details. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details. Args: verification_token: The verification token to check - (optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation) + (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token @@ -122,12 +122,12 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles slack_bolt.middleware.ssl_check.ssl_check requests. -Refer to https://api.slack.com/interactivity/slash-commands for details.

    +Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation)
    base_logger
    The base logger
    diff --git a/docs/reference/middleware/ssl_check/ssl_check.html b/docs/reference/middleware/ssl_check/ssl_check.html index 72b98724a..f90ad4d87 100644 --- a/docs/reference/middleware/ssl_check/ssl_check.html +++ b/docs/reference/middleware/ssl_check/ssl_check.html @@ -65,11 +65,11 @@

    Classes

    base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. - Refer to https://api.slack.com/interactivity/slash-commands for details. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details. Args: verification_token: The verification token to check - (optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation) + (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token @@ -111,12 +111,12 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles ssl_check requests. -Refer to https://api.slack.com/interactivity/slash-commands for details.

    +Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details.

    Args

    verification_token
    The verification token to check -(optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation)
    +(optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation)
    base_logger
    The base logger
    diff --git a/docs/reference/middleware/url_verification/async_url_verification.html b/docs/reference/middleware/url_verification/async_url_verification.html index e7fbb82fe..d1408052d 100644 --- a/docs/reference/middleware/url_verification/async_url_verification.html +++ b/docs/reference/middleware/url_verification/async_url_verification.html @@ -73,7 +73,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://api.slack.com/events/url_verification for details.

    +

    Refer to https://docs.slack.dev/reference/events/url_verification/ for details.

    Args

    base_logger
    diff --git a/docs/reference/middleware/url_verification/index.html b/docs/reference/middleware/url_verification/index.html index 9e08c1699..480c861d6 100644 --- a/docs/reference/middleware/url_verification/index.html +++ b/docs/reference/middleware/url_verification/index.html @@ -70,7 +70,7 @@

    Classes

    def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. - Refer to https://api.slack.com/events/url_verification for details. + Refer to https://docs.slack.dev/reference/events/url_verification/ for details. Args: base_logger: The base logger @@ -104,7 +104,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://api.slack.com/events/url_verification for details.

    +

    Refer to https://docs.slack.dev/reference/events/url_verification/ for details.

    Args

    base_logger
    diff --git a/docs/reference/middleware/url_verification/url_verification.html b/docs/reference/middleware/url_verification/url_verification.html index e90bf0395..ff22c2986 100644 --- a/docs/reference/middleware/url_verification/url_verification.html +++ b/docs/reference/middleware/url_verification/url_verification.html @@ -59,7 +59,7 @@

    Classes

    def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. - Refer to https://api.slack.com/events/url_verification for details. + Refer to https://docs.slack.dev/reference/events/url_verification/ for details. Args: base_logger: The base logger @@ -93,7 +93,7 @@

    Classes

    A middleware can process request data before other middleware and listener functions.

    Handles url_verification requests.

    -

    Refer to https://api.slack.com/events/url_verification for details.

    +

    Refer to https://docs.slack.dev/reference/events/url_verification/ for details.

    Args

    base_logger
    diff --git a/docs/reference/oauth/index.html b/docs/reference/oauth/index.html index d118a5e72..d53dc6a41 100644 --- a/docs/reference/oauth/index.html +++ b/docs/reference/oauth/index.html @@ -37,7 +37,7 @@

    Module slack_bolt.oauth

    Slack OAuth flow support for building an app that is installable in any workspaces.

    -

    Refer to https://slack.dev/bolt-python/concepts#authenticating-oauth for details.

    +

    Refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth for details.

    Sub-modules

    diff --git a/docs/reference/request/index.html b/docs/reference/request/index.html index 06d9b4933..84cd15050 100644 --- a/docs/reference/request/index.html +++ b/docs/reference/request/index.html @@ -37,7 +37,7 @@

    Module slack_bolt.request

    Incoming request from Slack through either HTTP request or Socket Mode connection.

    -

    Refer to https://api.slack.com/apis/connections for the two types of connections. +

    Refer to https://docs.slack.dev/apis/events-api/ for the two types of connections. This interface encapsulates the difference between the two.

    diff --git a/docs/reference/response/index.html b/docs/reference/response/index.html index c986c7150..a4f4989ee 100644 --- a/docs/reference/response/index.html +++ b/docs/reference/response/index.html @@ -39,7 +39,7 @@

    Module slack_bolt.response

    This interface represents Bolt's synchronous response to Slack.

    In Socket Mode, the response data can be transformed to a WebSocket message. In the HTTP endpoint mode, the response data becomes an HTTP response data.

    -

    Refer to https://api.slack.com/apis/connections for the two types of connections.

    +

    Refer to https://docs.slack.dev/apis/events-api/ for the two types of connections.

    Sub-modules

    diff --git a/docs/reference/workflows/index.html b/docs/reference/workflows/index.html index caaffe74d..0dfe7457f 100644 --- a/docs/reference/workflows/index.html +++ b/docs/reference/workflows/index.html @@ -43,7 +43,7 @@

    Module slack_bolt.workflows

  • slack_bolt.workflows.step.utilities
  • slack_bolt.workflows.step.async_step (if you use asyncio-based AsyncApp)
  • -

    Refer to https://api.slack.com/workflows/steps for details.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    Sub-modules

    diff --git a/docs/reference/workflows/step/async_step.html b/docs/reference/workflows/step/async_step.html index 3bf597134..18fdd3ab9 100644 --- a/docs/reference/workflows/step/async_step.html +++ b/docs/reference/workflows/step/async_step.html @@ -78,7 +78,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Args: callback_id: The callback_id for this step from app @@ -124,7 +124,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ """ return AsyncWorkflowStepBuilder(callback_id, base_logger=base_logger) @@ -200,7 +200,7 @@

    Classes

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Args

    callback_id
    @@ -252,7 +252,7 @@

    Static methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    @@ -267,7 +267,7 @@

    Static methods

    class AsyncWorkflowStepBuilder:
         """Steps from apps
    -    Refer to https://api.slack.com/workflows/steps for details.
    +    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.
         """
     
         callback_id: Union[str, Pattern]
    @@ -285,7 +285,7 @@ 

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ This builder is supposed to be used as decorator. @@ -327,7 +327,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new edit listener with details. @@ -380,7 +380,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new save listener with details. @@ -433,7 +433,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new execute listener with details. @@ -480,7 +480,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -555,10 +555,10 @@

    Static methods

    return _middleware

    Steps from apps -Refer to https://api.slack.com/workflows/steps for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    This builder is supposed to be used as decorator.

    my_step = AsyncWorkflowStep.builder("my_step")
     @my_step.edit
    @@ -659,7 +659,7 @@ 

    Methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -685,7 +685,7 @@

    Methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object.

    Returns

    @@ -709,7 +709,7 @@

    Returns

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new edit listener with details. @@ -754,7 +754,7 @@

    Returns

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new edit listener with details.

    You can use this method as decorator as well.

    @my_step.edit
    @@ -799,7 +799,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new execute listener with details. @@ -844,7 +844,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new execute listener with details.

    You can use this method as decorator as well.

    @my_step.execute
    @@ -889,7 +889,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new save listener with details. @@ -934,7 +934,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new save listener with details.

    You can use this method as decorator as well.

    @my_step.save
    diff --git a/docs/reference/workflows/step/index.html b/docs/reference/workflows/step/index.html
    index 62d989976..50b52906b 100644
    --- a/docs/reference/workflows/step/index.html
    +++ b/docs/reference/workflows/step/index.html
    @@ -174,7 +174,7 @@ 

    Classes

    ) app.step(ws) - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: WebClient, body: dict): @@ -219,7 +219,7 @@

    Classes

    ) app.step(ws)
    -

    Refer to https://api.slack.com/workflows/steps for details.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    class Fail @@ -411,7 +411,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Args: callback_id: The callback_id for this step from app @@ -453,7 +453,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ """ return WorkflowStepBuilder( callback_id, @@ -546,7 +546,7 @@

    Classes

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Args

    callback_id
    @@ -598,7 +598,7 @@

    Static methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    diff --git a/docs/reference/workflows/step/step.html b/docs/reference/workflows/step/step.html index 6e1567bd6..0309acd88 100644 --- a/docs/reference/workflows/step/step.html +++ b/docs/reference/workflows/step/step.html @@ -78,7 +78,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Args: callback_id: The callback_id for this step from app @@ -120,7 +120,7 @@

    Classes

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ """ return WorkflowStepBuilder( callback_id, @@ -213,7 +213,7 @@

    Classes

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Args

    callback_id
    @@ -265,7 +265,7 @@

    Static methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    @@ -280,7 +280,7 @@

    Static methods

    class WorkflowStepBuilder:
         """Steps from apps
    -    Refer to https://api.slack.com/workflows/steps for details.
    +    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.
         """
     
         callback_id: Union[str, Pattern]
    @@ -298,7 +298,7 @@ 

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ This builder is supposed to be used as decorator. @@ -340,7 +340,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new edit listener with details. @@ -394,7 +394,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new save listener with details. @@ -447,7 +447,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new execute listener with details. @@ -494,7 +494,7 @@

    Static methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -584,10 +584,10 @@

    Static methods

    return _middleware

    Steps from apps -Refer to https://api.slack.com/workflows/steps for details.

    +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    This builder is supposed to be used as decorator.

    my_step = WorkflowStep.builder("my_step")
     @my_step.edit
    @@ -703,7 +703,7 @@ 

    Methods

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -729,7 +729,7 @@

    Methods

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object.

    Returns

    @@ -753,7 +753,7 @@

    Returns

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new edit listener with details. @@ -799,7 +799,7 @@

    Returns

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new edit listener with details.

    You can use this method as decorator as well.

    @my_step.edit
    @@ -844,7 +844,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new execute listener with details. @@ -889,7 +889,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new execute listener with details.

    You can use this method as decorator as well.

    @my_step.execute
    @@ -934,7 +934,7 @@ 

    Args

    """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new save listener with details. @@ -979,7 +979,7 @@

    Args

    Deprecated

    Steps from apps for legacy workflows are now deprecated. -Use new custom steps: https://api.slack.com/automation/functions/custom-bolt

    +Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/

    Registers a new save listener with details.

    You can use this method as decorator as well.

    @my_step.save
    diff --git a/docs/reference/workflows/step/utilities/async_configure.html b/docs/reference/workflows/step/utilities/async_configure.html
    index 008c35ab5..10f236c47 100644
    --- a/docs/reference/workflows/step/utilities/async_configure.html
    +++ b/docs/reference/workflows/step/utilities/async_configure.html
    @@ -83,7 +83,7 @@ 

    Classes

    ) app.step(ws) - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: AsyncWebClient, body: dict): @@ -131,7 +131,7 @@

    Classes

    ) app.step(ws)
    -

    Refer to https://api.slack.com/workflows/steps for details.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/docs/reference/workflows/step/utilities/configure.html b/docs/reference/workflows/step/utilities/configure.html index 26d646cf2..258bce312 100644 --- a/docs/reference/workflows/step/utilities/configure.html +++ b/docs/reference/workflows/step/utilities/configure.html @@ -83,7 +83,7 @@

    Classes

    ) app.step(ws) - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: WebClient, body: dict): @@ -128,7 +128,7 @@

    Classes

    ) app.step(ws)
    -

    Refer to https://api.slack.com/workflows/steps for details.

    +

    Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details.

    diff --git a/examples/aws_lambda/README.md b/examples/aws_lambda/README.md index 454f080a7..49a8f7da2 100644 --- a/examples/aws_lambda/README.md +++ b/examples/aws_lambda/README.md @@ -32,16 +32,16 @@ Instructions on how to set up and deploy each example are provided below. `lazy_aws_lambda_config.yaml` - Optionally enter a description for the role, such as "Bolt Python basic role" -3. Ensure you have created an app on api.slack.com/apps as per the [Getting - Started Guide](https://slack.dev/bolt-python/tutorial/getting-started). +3. Ensure you have created an app on api.slack.com/apps as per the + [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. Ensure you have installed it to a workspace. 4. Ensure you have exported your Slack Bot Token and Slack Signing Secret for your apps as the environment variables `SLACK_BOT_TOKEN` and - `SLACK_SIGNING_SECRET`, respectively, as per the [Getting - Started Guide](https://slack.dev/bolt-python/tutorial/getting-started). + `SLACK_SIGNING_SECRET`, respectively, as per the + [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. 5. You may want to create a dedicated virtual environment for this example app, as - per the "Setting up your project" section of the [Getting - Started Guide](https://slack.dev/bolt-python/tutorial/getting-started). + per the "Setting up your project" section of the + [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. 6. Let's deploy the Lambda! Run `./deploy_lazy.sh`. By default it deploys to the us-east-1 region in AWS - you can change this at the top of `lazy_aws_lambda_config.yaml` if you wish. 7. Load up AWS Lambda inside the AWS Console - make sure you are in the correct @@ -150,7 +150,7 @@ Let’s create a user role that will use the custom policy we created as well as 3. "Create Role" ### Create Slack App and Load your Lambda to AWS -Ensure you have created an app on [api.slack.com/apps](https://api.slack.com/apps) as per the [Getting Started Guide](https://slack.dev/bolt-python/tutorial/getting-started). You do not need to ensure you have installed it to a workspace, as the OAuth flow will provide your app the ability to be installed by anyone. +Ensure you have created an app on [api.slack.com/apps](https://api.slack.com/apps) as per the [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. You do not need to ensure you have installed it to a workspace, as the OAuth flow will provide your app the ability to be installed by anyone. 1. Remember those S3 buckets we made? You will need the names of these buckets again in the next step. 2. You need many environment variables exported! Specifically the following from api.slack.com/apps diff --git a/examples/django/README.md b/examples/django/README.md index c50681c5b..ca0460fd1 100644 --- a/examples/django/README.md +++ b/examples/django/README.md @@ -4,7 +4,7 @@ This example demonstrates how you can use Bolt for Python in your Django applica ### `simple_app` - Single-workspace App Example -If you want to run a simple app like the one you've tried in the [Getting Started Guide](https://slack.dev/bolt-python/tutorial/getting-started), this is the right one for you. By default, this Django project runs this application. If you want to switch to OAuth flow supported one, modify `myslackapp/urls.py`. +If you want to run a simple app like the one you've tried in the [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide, this is the right one for you. By default, this Django project runs this application. If you want to switch to OAuth flow supported one, modify `myslackapp/urls.py`. To run this app, all you need to do are: @@ -31,7 +31,7 @@ python manage.py migrate python manage.py runserver 0.0.0.0:3000 ``` -As you did at [Getting Started Guide](https://slack.dev/bolt-python/tutorial/getting-started), configure ngrok or something similar to serve a public endpoint. Lastly, +As you did at [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide, configure ngrok or something similar to serve a public endpoint. Lastly, * Go back to the Slack app configuration page * Go to "Event Subscriptions" @@ -54,7 +54,7 @@ To run this app, all you need to do are: * Create a new Slack app configuration at https://api.slack.com/apps?new_app=1 * Go to "OAuth & Permissions" * Add `app_mentions:read`, `chat:write` in Scopes > Bot Token Scopes -* Follow the instructions [here](https://slack.dev/bolt-python/concepts#authenticating-oauth) for configuring OAuth flow supported Slack apps +* Follow the instructions [here](https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth) for configuring OAuth flow supported Slack apps You can start your Django application this way: @@ -73,7 +73,7 @@ python manage.py migrate python manage.py runserver 0.0.0.0:3000 ``` -As you did at [Getting Started Guide](https://slack.dev/bolt-python/tutorial/getting-started), configure ngrok or something similar to serve a public endpoint. Lastly, +As you did at [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide, configure ngrok or something similar to serve a public endpoint. Lastly, * Go back to the Slack app configuration page * Go to "Event Subscriptions" diff --git a/examples/getting_started/README.md b/examples/getting_started/README.md index 63875a4dd..5d3c2f61d 100644 --- a/examples/getting_started/README.md +++ b/examples/getting_started/README.md @@ -1,5 +1,5 @@ # Getting Started with ⚡️ Bolt for Python -> Slack app example from 📚 [Getting started with Bolt for Python][1] +> Slack app example from 📚 [Building an App with Bolt for Python][1] ## Overview @@ -42,6 +42,6 @@ ngrok http 3000 python3 app.py ``` -[1]: https://slack.dev/bolt-python/tutorial/getting-started -[2]: https://slack.dev/bolt-python/ -[3]: https://slack.dev/bolt-python/tutorial/getting-started#setting-up-events +[1]: https://docs.slack.dev/tools/bolt-python/building-an-app +[2]: https://docs.slack.dev/tools/bolt-python/ +[3]: https://docs.slack.dev/tools/bolt-python/building-an-app#setting-up-events diff --git a/examples/message_events.py b/examples/message_events.py index 7658be276..3fd424060 100644 --- a/examples/message_events.py +++ b/examples/message_events.py @@ -32,7 +32,7 @@ def extract_subtype(body: dict, context: BoltContext, next: Callable): next() -# https://api.slack.com/events/message +# https://docs.slack.dev/reference/events/message/ # Newly posted messages only # or @app.event("message") @app.event({"type": "message", "subtype": None}) @@ -55,8 +55,8 @@ def detect_deletion(say: Say, body: dict): say(f"You've deleted a message: {text}") -# https://api.slack.com/events/message/file_share -# https://api.slack.com/events/message/bot_message +# https://docs.slack.dev/reference/events/message/file_share +# https://docs.slack.dev/reference/events/message/bot_message @app.event( event={"type": "message", "subtype": re.compile("(me_message)|(file_share)")}, middleware=[extract_subtype], diff --git a/examples/readme_app.py b/examples/readme_app.py index 963938658..fe81a0904 100644 --- a/examples/readme_app.py +++ b/examples/readme_app.py @@ -16,13 +16,13 @@ def log_request(logger, body, next): return next() -# Events API: https://api.slack.com/events-api +# Events API: https://docs.slack.dev/apis/events-api/ @app.event("app_mention") def event_test(say): say("What's up?") -# Interactivity: https://api.slack.com/interactivity +# Interactivity: https://docs.slack.dev/interactivity/ @app.shortcut("callback-id-here") # @app.command("/hello-bolt-python") def open_modal(ack, client, logger, body): diff --git a/examples/readme_async_app.py b/examples/readme_async_app.py index c43d3af32..f11d308a0 100644 --- a/examples/readme_async_app.py +++ b/examples/readme_async_app.py @@ -28,13 +28,13 @@ async def log_request(logger, body, next): return await next() -# Events API: https://api.slack.com/events-api +# Events API: https://docs.slack.dev/apis/events-api/ @app.event("app_mention") async def event_test(say): await say("What's up?") -# Interactivity: https://api.slack.com/interactivity +# Interactivity: https://docs.slack.dev/interactivity/ @app.shortcut("callback-id-here") # @app.command("/hello-bolt-python") async def open_modal(ack, client, logger, body): diff --git a/examples/workflow_steps/async_steps_from_apps.py b/examples/workflow_steps/async_steps_from_apps.py index 11566de6c..ed108cf5e 100644 --- a/examples/workflow_steps/async_steps_from_apps.py +++ b/examples/workflow_steps/async_steps_from_apps.py @@ -11,7 +11,7 @@ ################################################################################ # Steps from apps for legacy workflows are now deprecated. # -# Use new custom steps: https://api.slack.com/automation/functions/custom-bolt # +# Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ # ################################################################################ logging.basicConfig(level=logging.DEBUG) diff --git a/examples/workflow_steps/async_steps_from_apps_decorator.py b/examples/workflow_steps/async_steps_from_apps_decorator.py index 423048a47..e04884723 100644 --- a/examples/workflow_steps/async_steps_from_apps_decorator.py +++ b/examples/workflow_steps/async_steps_from_apps_decorator.py @@ -13,7 +13,7 @@ ################################################################################ # Steps from apps for legacy workflows are now deprecated. # -# Use new custom steps: https://api.slack.com/automation/functions/custom-bolt # +# Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ # ################################################################################ logging.basicConfig(level=logging.DEBUG) diff --git a/examples/workflow_steps/async_steps_from_apps_primitive.py b/examples/workflow_steps/async_steps_from_apps_primitive.py index 2e636e600..06a2956db 100644 --- a/examples/workflow_steps/async_steps_from_apps_primitive.py +++ b/examples/workflow_steps/async_steps_from_apps_primitive.py @@ -5,7 +5,7 @@ ################################################################################ # Steps from apps for legacy workflows are now deprecated. # -# Use new custom steps: https://api.slack.com/automation/functions/custom-bolt # +# Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ # ################################################################################ logging.basicConfig(level=logging.DEBUG) diff --git a/examples/workflow_steps/steps_from_apps.py b/examples/workflow_steps/steps_from_apps.py index efbb2ce65..b5f591700 100644 --- a/examples/workflow_steps/steps_from_apps.py +++ b/examples/workflow_steps/steps_from_apps.py @@ -8,7 +8,7 @@ ################################################################################ # Steps from apps for legacy workflows are now deprecated. # -# Use new custom steps: https://api.slack.com/automation/functions/custom-bolt # +# Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ # ################################################################################ logging.basicConfig(level=logging.DEBUG) diff --git a/examples/workflow_steps/steps_from_apps_decorator.py b/examples/workflow_steps/steps_from_apps_decorator.py index 64ddfcc20..1558e825a 100644 --- a/examples/workflow_steps/steps_from_apps_decorator.py +++ b/examples/workflow_steps/steps_from_apps_decorator.py @@ -9,7 +9,7 @@ ################################################################################ # Steps from apps for legacy workflows are now deprecated. # -# Use new custom steps: https://api.slack.com/automation/functions/custom-bolt # +# Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ # ################################################################################ logging.basicConfig(level=logging.DEBUG) diff --git a/examples/workflow_steps/steps_from_apps_primitive.py b/examples/workflow_steps/steps_from_apps_primitive.py index 6aa5a98bb..dd4231ba6 100644 --- a/examples/workflow_steps/steps_from_apps_primitive.py +++ b/examples/workflow_steps/steps_from_apps_primitive.py @@ -7,7 +7,7 @@ ################################################################################ # Steps from apps for legacy workflows are now deprecated. # -# Use new custom steps: https://api.slack.com/automation/functions/custom-bolt # +# Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ # ################################################################################ logging.basicConfig(level=logging.DEBUG) diff --git a/pyproject.toml b/pyproject.toml index 024ee6654..5a6523f35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = ["slack_sdk>=3.35.0,<4"] [project.urls] -Documentation = "https://slack.dev/bolt-python" +Documentation = "https://docs.slack.dev/tools/bolt-python/" [tool.setuptools.packages.find] include = ["slack_bolt*"] diff --git a/slack_bolt/__init__.py b/slack_bolt/__init__.py index 32ab76721..6331925f8 100644 --- a/slack_bolt/__init__.py +++ b/slack_bolt/__init__.py @@ -1,7 +1,7 @@ """ -A Python framework to build Slack apps in a flash with the latest platform features.Read the [getting started guide](https://slack.dev/bolt-python/tutorial/getting-started) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. +A Python framework to build Slack apps in a flash with the latest platform features.Read the [getting started guide](https://docs.slack.dev/tools/bolt-python/building-an-app) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. -* Website: https://slack.dev/bolt-python/ +* Website: https://docs.slack.dev/tools/bolt-python/ * GitHub repository: https://github.com/slackapi/bolt-python * The class representing a Bolt app: `slack_bolt.app.app` """ # noqa: E501 diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index 60f20ea9e..5a7f32917 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -159,10 +159,10 @@ def message_hello(message, say): if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://slack.dev/bolt-python/tutorial/getting-started for details. + Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, - refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app. + refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. @@ -671,7 +671,7 @@ def middleware_func(logger, body, next): # Pass a function to this method app.middleware(middleware_func) - Refer to https://slack.dev/bolt-python/concepts#global-middleware for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -717,7 +717,7 @@ def step( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -735,7 +735,7 @@ def step( # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -752,7 +752,7 @@ def step( warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -829,7 +829,7 @@ def ask_for_introduction(event, say): # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -867,7 +867,7 @@ def say_hello(message, say): # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -978,7 +978,7 @@ def repeat_text(ack, say, command): # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1025,7 +1025,7 @@ def open_modal(ack, body, client): # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1093,9 +1093,9 @@ def update_message(ack): # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1121,7 +1121,7 @@ def block_action( middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1138,7 +1138,7 @@ def attachment_action( middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1154,7 +1154,7 @@ def dialog_submission( middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1170,7 +1170,7 @@ def dialog_cancellation( middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1211,7 +1211,7 @@ def handle_submission(ack, body, client, view): # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1237,7 +1237,9 @@ def view_submission( middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1253,7 +1255,7 @@ def view_closed( middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1294,8 +1296,8 @@ def show_menu_options(ack): Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. @@ -1335,7 +1337,7 @@ def dialog_suggestion( middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 906359fcc..39f3c3c0e 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -165,10 +165,10 @@ async def message_hello(message, say): # async function if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://slack.dev/bolt-python/concepts#async for details. + Refer to https://docs.slack.dev/tools/bolt-python/concepts/async for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, - refer to https://slack.dev/bolt-python/concepts#authenticating-oauth to learn how to configure the app. + refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. @@ -738,7 +738,7 @@ def step( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. @@ -756,7 +756,7 @@ def step( # Pass Step to set up listeners app.step(ws) - Refer to https://api.slack.com/workflows/steps for details of steps from apps. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. For further information about AsyncWorkflowStep specific function arguments @@ -772,7 +772,7 @@ def step( warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " - "Use new custom steps: https://api.slack.com/automation/functions/custom-bolt" + "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) @@ -854,7 +854,7 @@ async def ask_for_introduction(event, say): # Pass a function to this method app.event("team_join")(ask_for_introduction) - Refer to https://api.slack.com/apis/connections/events-api for details of Events API. + Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -892,7 +892,7 @@ async def say_hello(message, say): # Pass a function to this method app.message(":wave:")(say_hello) - Refer to https://api.slack.com/events/message for details of `message` events. + Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1007,7 +1007,7 @@ async def repeat_text(ack, say, command): # Pass a function to this method app.command("/echo")(repeat_text) - Refer to https://api.slack.com/interactivity/slash-commands for details of Slash Commands. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1054,7 +1054,7 @@ async def open_modal(ack, body, client): # Pass a function to this method app.shortcut("open_modal")(open_modal) - Refer to https://api.slack.com/interactivity/shortcuts for details about Shortcuts. + Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1122,9 +1122,9 @@ async def update_message(ack): # Pass a function to this method app.action("approve_button")(update_message) - * Refer to https://api.slack.com/reference/interaction-payloads/block-actions for actions in `blocks`. - * Refer to https://api.slack.com/legacy/message-buttons for actions in `attachments`. - * Refer to https://api.slack.com/dialogs for actions in dialogs. + * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. + * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. + * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1150,7 +1150,7 @@ def block_action( middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `block_actions` action listener. - Refer to https://api.slack.com/reference/interaction-payloads/block-actions for details. + Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): @@ -1167,7 +1167,7 @@ def attachment_action( middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `interactive_message` action listener. - Refer to https://api.slack.com/legacy/message-buttons for details.""" + Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1183,7 +1183,7 @@ def dialog_submission( middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1199,7 +1199,7 @@ def dialog_cancellation( middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_submission` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1240,7 +1240,7 @@ async def handle_submission(ack, body, client, view): # Pass a function to this method app.view("view_1")(handle_submission) - Refer to https://api.slack.com/reference/interaction-payloads/views for details of payloads. + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1266,7 +1266,9 @@ def view_submission( middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1282,7 +1284,7 @@ def view_closed( middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_closed` listener. - Refer to https://api.slack.com/reference/interaction-payloads/views#view_closed for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -1323,8 +1325,8 @@ async def show_menu_options(ack): Refer to the following documents for details: - * https://api.slack.com/reference/block-kit/block-elements#external_select - * https://api.slack.com/reference/block-kit/block-elements#external_multi_select + * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select + * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.async_args`'s API document. @@ -1364,7 +1366,7 @@ def dialog_suggestion( middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `dialog_suggestion` listener. - Refer to https://api.slack.com/dialogs for details.""" + Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) diff --git a/slack_bolt/authorization/__init__.py b/slack_bolt/authorization/__init__.py index a936a866b..4b80a93bb 100644 --- a/slack_bolt/authorization/__init__.py +++ b/slack_bolt/authorization/__init__.py @@ -1,7 +1,7 @@ """Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack event. -Refer to https://slack.dev/bolt-python/concepts#authorization for details. +Refer to https://docs.slack.dev/tools/bolt-python/concepts/authorization for details. """ from .authorize_result import AuthorizeResult diff --git a/slack_bolt/context/__init__.py b/slack_bolt/context/__init__.py index fb3337c7a..865825601 100644 --- a/slack_bolt/context/__init__.py +++ b/slack_bolt/context/__init__.py @@ -2,7 +2,7 @@ Bolt automatically attaches information that is included in the incoming event, like `user_id`, `team_id`, `channel_id`, and `enterprise_id`. -Refer to https://slack.dev/bolt-python/concepts#context for details. +Refer to https://docs.slack.dev/tools/bolt-python/concepts/context for details. """ # Don't add async module imports here diff --git a/slack_bolt/lazy_listener/__init__.py b/slack_bolt/lazy_listener/__init__.py index 4d9111cc3..a92c18483 100644 --- a/slack_bolt/lazy_listener/__init__.py +++ b/slack_bolt/lazy_listener/__init__.py @@ -19,7 +19,7 @@ def run_long_process(respond, body): lazy=[run_long_process] ) -Refer to https://slack.dev/bolt-python/concepts#lazy-listeners for more details. +Refer to https://docs.slack.dev/tools/bolt-python/concepts/lazy-listeners for more details. """ # Don't add async module imports here diff --git a/slack_bolt/listener_matcher/builtins.py b/slack_bolt/listener_matcher/builtins.py index 57dbdf4f1..76c12d452 100644 --- a/slack_bolt/listener_matcher/builtins.py +++ b/slack_bolt/listener_matcher/builtins.py @@ -294,7 +294,7 @@ def func(body: Dict[str, Any]) -> bool: return dialog_submission(constraints["callback_id"], asyncio) if action_type == "dialog_cancellation": return dialog_cancellation(constraints["callback_id"], asyncio) - # https://api.slack.com/workflows/steps + # https://docs.slack.dev/legacy/legacy-steps-from-apps/ if action_type == "workflow_step_edit": return workflow_step_edit(constraints["callback_id"], asyncio) diff --git a/slack_bolt/logger/messages.py b/slack_bolt/logger/messages.py index d30f51acb..80e68d022 100644 --- a/slack_bolt/logger/messages.py +++ b/slack_bolt/logger/messages.py @@ -348,7 +348,7 @@ def info_default_oauth_settings_loaded() -> str: "Bolt has enabled the file-based InstallationStore/OAuthStateStore for you. " "Note that these file-based stores are for local development. " "If you'd like to use a different data store, set the oauth_settings argument in the App constructor. " - "Please refer to https://slack.dev/bolt-python/concepts#authenticating-oauth for more details." + "Please refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth for more details." ) diff --git a/slack_bolt/middleware/request_verification/async_request_verification.py b/slack_bolt/middleware/request_verification/async_request_verification.py index 68484bde0..3fb9e209b 100644 --- a/slack_bolt/middleware/request_verification/async_request_verification.py +++ b/slack_bolt/middleware/request_verification/async_request_verification.py @@ -10,7 +10,7 @@ class AsyncRequestVerification(RequestVerification, AsyncMiddleware): """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. + Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. """ async def async_process( diff --git a/slack_bolt/middleware/request_verification/request_verification.py b/slack_bolt/middleware/request_verification/request_verification.py index 5662dcf08..2cf7e361e 100644 --- a/slack_bolt/middleware/request_verification/request_verification.py +++ b/slack_bolt/middleware/request_verification/request_verification.py @@ -14,7 +14,7 @@ def __init__(self, signing_secret: str, base_logger: Optional[Logger] = None): """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. - Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details. + Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. Args: signing_secret: The signing secret diff --git a/slack_bolt/middleware/ssl_check/ssl_check.py b/slack_bolt/middleware/ssl_check/ssl_check.py index d608e5c3d..88c5105ef 100644 --- a/slack_bolt/middleware/ssl_check/ssl_check.py +++ b/slack_bolt/middleware/ssl_check/ssl_check.py @@ -17,11 +17,11 @@ def __init__( base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. - Refer to https://api.slack.com/interactivity/slash-commands for details. + Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details. Args: verification_token: The verification token to check - (optional as it's already deprecated - https://api.slack.com/authentication/verifying-requests-from-slack#verification_token_deprecation) + (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token diff --git a/slack_bolt/middleware/url_verification/url_verification.py b/slack_bolt/middleware/url_verification/url_verification.py index e59398fd7..7505c9c15 100644 --- a/slack_bolt/middleware/url_verification/url_verification.py +++ b/slack_bolt/middleware/url_verification/url_verification.py @@ -11,7 +11,7 @@ class UrlVerification(Middleware): def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. - Refer to https://api.slack.com/events/url_verification for details. + Refer to https://docs.slack.dev/reference/events/url_verification/ for details. Args: base_logger: The base logger diff --git a/slack_bolt/oauth/__init__.py b/slack_bolt/oauth/__init__.py index c4f806698..0a5c3db07 100644 --- a/slack_bolt/oauth/__init__.py +++ b/slack_bolt/oauth/__init__.py @@ -1,6 +1,6 @@ """Slack OAuth flow support for building an app that is installable in any workspaces. -Refer to https://slack.dev/bolt-python/concepts#authenticating-oauth for details. +Refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth for details. """ # Don't add async module imports here diff --git a/slack_bolt/request/__init__.py b/slack_bolt/request/__init__.py index ee8b435a7..8610b6019 100644 --- a/slack_bolt/request/__init__.py +++ b/slack_bolt/request/__init__.py @@ -1,6 +1,6 @@ """Incoming request from Slack through either HTTP request or Socket Mode connection. -Refer to https://api.slack.com/apis/connections for the two types of connections. +Refer to https://docs.slack.dev/apis/events-api/ for the two types of connections. This interface encapsulates the difference between the two. """ diff --git a/slack_bolt/response/__init__.py b/slack_bolt/response/__init__.py index 373acccf2..c390b2d8e 100644 --- a/slack_bolt/response/__init__.py +++ b/slack_bolt/response/__init__.py @@ -3,7 +3,7 @@ In Socket Mode, the response data can be transformed to a WebSocket message. In the HTTP endpoint mode, the response data becomes an HTTP response data. -Refer to https://api.slack.com/apis/connections for the two types of connections. +Refer to https://docs.slack.dev/apis/events-api/ for the two types of connections. """ from .response import BoltResponse diff --git a/slack_bolt/workflows/__init__.py b/slack_bolt/workflows/__init__.py index 97e6ec765..c0f6d96b7 100644 --- a/slack_bolt/workflows/__init__.py +++ b/slack_bolt/workflows/__init__.py @@ -6,5 +6,5 @@ * `slack_bolt.workflows.step.utilities` * `slack_bolt.workflows.step.async_step` (if you use asyncio-based `AsyncApp`) -Refer to https://api.slack.com/workflows/steps for details. +Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ diff --git a/slack_bolt/workflows/step/async_step.py b/slack_bolt/workflows/step/async_step.py index 250d2e900..7fa0ed858 100644 --- a/slack_bolt/workflows/step/async_step.py +++ b/slack_bolt/workflows/step/async_step.py @@ -29,7 +29,7 @@ class AsyncWorkflowStepBuilder: """Steps from apps - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ callback_id: Union[str, Pattern] @@ -47,7 +47,7 @@ def __init__( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ This builder is supposed to be used as decorator. @@ -89,7 +89,7 @@ def edit( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new edit listener with details. @@ -142,7 +142,7 @@ def save( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new save listener with details. @@ -195,7 +195,7 @@ def execute( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new execute listener with details. @@ -242,7 +242,7 @@ def build(self, base_logger: Optional[Logger] = None) -> "AsyncWorkflowStep": """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -340,7 +340,7 @@ def __init__( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Args: callback_id: The callback_id for this step from app @@ -386,7 +386,7 @@ def builder( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ """ return AsyncWorkflowStepBuilder(callback_id, base_logger=base_logger) diff --git a/slack_bolt/workflows/step/step.py b/slack_bolt/workflows/step/step.py index 7cdbb913c..4fca25717 100644 --- a/slack_bolt/workflows/step/step.py +++ b/slack_bolt/workflows/step/step.py @@ -24,7 +24,7 @@ class WorkflowStepBuilder: """Steps from apps - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ callback_id: Union[str, Pattern] @@ -42,7 +42,7 @@ def __init__( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ This builder is supposed to be used as decorator. @@ -84,7 +84,7 @@ def edit( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new edit listener with details. @@ -138,7 +138,7 @@ def save( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new save listener with details. @@ -191,7 +191,7 @@ def execute( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new execute listener with details. @@ -238,7 +238,7 @@ def build(self, base_logger: Optional[Logger] = None) -> "WorkflowStep": """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Constructs a WorkflowStep object. This method may raise an exception if the builder doesn't have enough configurations to build the object. @@ -351,7 +351,7 @@ def __init__( """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Args: callback_id: The callback_id for this step from app @@ -393,7 +393,7 @@ def builder(cls, callback_id: Union[str, Pattern], base_logger: Optional[Logger] """ Deprecated: Steps from apps for legacy workflows are now deprecated. - Use new custom steps: https://api.slack.com/automation/functions/custom-bolt + Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ """ return WorkflowStepBuilder( callback_id, diff --git a/slack_bolt/workflows/step/utilities/async_configure.py b/slack_bolt/workflows/step/utilities/async_configure.py index 721d5049c..5b9a7f9ae 100644 --- a/slack_bolt/workflows/step/utilities/async_configure.py +++ b/slack_bolt/workflows/step/utilities/async_configure.py @@ -32,7 +32,7 @@ async def edit(ack, step, configure): ) app.step(ws) - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: AsyncWebClient, body: dict): diff --git a/slack_bolt/workflows/step/utilities/configure.py b/slack_bolt/workflows/step/utilities/configure.py index d44c8d0da..1280be8f7 100644 --- a/slack_bolt/workflows/step/utilities/configure.py +++ b/slack_bolt/workflows/step/utilities/configure.py @@ -32,7 +32,7 @@ def edit(ack, step, configure): ) app.step(ws) - Refer to https://api.slack.com/workflows/steps for details. + Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: WebClient, body: dict): diff --git a/tests/scenario_tests/test_attachment_actions.py b/tests/scenario_tests/test_attachment_actions.py index fa40187ee..f40deb22b 100644 --- a/tests/scenario_tests/test_attachment_actions.py +++ b/tests/scenario_tests/test_attachment_actions.py @@ -164,7 +164,7 @@ def test_failure_2(self): assert_auth_test_count(self, 1) -# https://api.slack.com/legacy/interactive-messages +# https://docs.slack.dev/legacy/legacy-messaging/legacy-making-messages-interactive/ body = { "type": "interactive_message", "actions": [ diff --git a/tests/scenario_tests_async/test_attachment_actions.py b/tests/scenario_tests_async/test_attachment_actions.py index f6613837b..c9817dcd7 100644 --- a/tests/scenario_tests_async/test_attachment_actions.py +++ b/tests/scenario_tests_async/test_attachment_actions.py @@ -191,7 +191,7 @@ async def test_failure_2(self): await assert_auth_test_count_async(self, 1) -# https://api.slack.com/legacy/interactive-messages +# https://docs.slack.dev/legacy/legacy-messaging/legacy-making-messages-interactive/ body = { "type": "interactive_message", "actions": [ From fc5bbc109cad1dffa1496c92e6da4ed7c5aea5fd Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 6 Oct 2025 16:32:48 -0700 Subject: [PATCH 154/282] feat: add ai-enabled features text streaming methods, feedback blocks, and loading state (#1387) Co-authored-by: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Co-authored-by: Maria Alejandra <104795114+srtaalej@users.noreply.github.com> Co-authored-by: Michael Brooks --- docs/english/concepts/ai-apps.md | 408 ++++++++++++++---- docs/english/concepts/message-sending.md | 61 ++- docs/reference/app/app.html | 11 +- docs/reference/app/async_app.html | 11 +- docs/reference/app/index.html | 11 +- docs/reference/async_app.html | 26 +- docs/reference/context/say/async_say.html | 2 + docs/reference/context/say/index.html | 2 + docs/reference/context/say/say.html | 2 + .../context/set_status/async_set_status.html | 11 +- docs/reference/context/set_status/index.html | 11 +- .../context/set_status/set_status.html | 11 +- .../async_set_suggested_prompts.html | 2 +- .../context/set_suggested_prompts/index.html | 2 +- .../set_suggested_prompts.html | 2 +- docs/reference/index.html | 26 +- pyproject.toml | 2 +- slack_bolt/context/say/async_say.py | 12 +- slack_bolt/context/say/say.py | 4 +- .../context/set_status/async_set_status.py | 13 +- slack_bolt/context/set_status/set_status.py | 13 +- .../async_set_suggested_prompts.py | 4 +- .../set_suggested_prompts.py | 4 +- tests/slack_bolt/context/test_say.py | 10 +- tests/slack_bolt/context/test_set_status.py | 38 ++ .../context/test_set_suggested_prompts.py | 37 ++ .../context/test_async_say.py | 13 +- .../context/test_async_set_status.py | 45 ++ .../test_async_set_suggested_prompts.py | 45 ++ 29 files changed, 693 insertions(+), 146 deletions(-) create mode 100644 tests/slack_bolt/context/test_set_status.py create mode 100644 tests/slack_bolt/context/test_set_suggested_prompts.py create mode 100644 tests/slack_bolt_async/context/test_async_set_status.py create mode 100644 tests/slack_bolt_async/context/test_async_set_suggested_prompts.py diff --git a/docs/english/concepts/ai-apps.md b/docs/english/concepts/ai-apps.md index b294c6688..44bd08df1 100644 --- a/docs/english/concepts/ai-apps.md +++ b/docs/english/concepts/ai-apps.md @@ -1,83 +1,195 @@ -# Using AI in Apps -:::info[This feature requires a paid plan] +# Using AI in Apps {#using-ai-in-apps} + +The Slack platform offers features tailored for AI agents and assistants. Your apps can [utilize the `Assistant` class](#assistant) for a side-panel view designed with AI in mind, or they can utilize features applicable to messages throughout Slack, like [chat streaming](#text-streaming) and [feedback buttons](#adding-and-handling-feedback). + +If you're unfamiliar with using these feature within Slack, you may want to read the [API documentation on the subject](/ai/). Then come back here to implement them with Bolt! + +## The `Assistant` class instance {#assistant} + +:::info[Some features within this guide require a paid plan] If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. ::: -The Agents & AI Apps feature comprises a unique messaging experience for Slack. If you're unfamiliar with using the Agents & AI Apps feature within Slack, you'll want to read the [API documentation on the subject](/ai/). Then come back here to implement them with Bolt! +The [`Assistant`](/tools/bolt-js/reference#the-assistantconfig-configuration-object) class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. -## Configuring your app to support AI features {#configuring-your-app} +A typical flow would look like: -1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & AI Apps** feature. +1. [The user starts a thread](#handling-new-thread). The `Assistant` class handles the incoming [`assistant_thread_started`](/reference/events/assistant_thread_started) event. +2. [The thread context may change at any point](#handling-thread-context-changes). The `Assistant` class can handle any incoming [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) events. The class also provides a default `context` store to keep track of thread context changes as the user moves through Slack. +3. [The user responds](#handling-user-response). The `Assistant` class handles the incoming [`message.im`](/reference/events/message.im) event. -2. Within the App Settings **OAuth & Permissions** page, add the following scopes: -* [`assistant:write`](/reference/scopes/assistant.write) -* [`chat:write`](/reference/scopes/chat.write) -* [`im:history`](/reference/scopes/im.history) -3. Within the App Settings **Event Subscriptions** page, subscribe to the following events: -* [`assistant_thread_started`](/reference/events/assistant_thread_started) -* [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) -* [`message.im`](/reference/events/message.im) +```python +assistant = Assistant() -:::info[You _could_ implement your own AI app by [listening](event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events (see implementation details below).] +# This listener is invoked when a human user opened an assistant thread +@assistant.thread_started +def start_assistant_thread( + say: Say, + get_thread_context: GetThreadContext, + set_suggested_prompts: SetSuggestedPrompts, + logger: logging.Logger, +): + try: + ... -That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! +# This listener is invoked when the human user sends a reply in the assistant thread +@assistant.user_message +def respond_in_assistant_thread( + client: WebClient, + context: BoltContext, + get_thread_context: GetThreadContext, + logger: logging.Logger, + payload: dict, + say: Say, + set_status: SetStatus, +): + try: + ... + +# Enable this assistant middleware in your Bolt app +app.use(assistant) +``` + +:::info[Consider the following] +You _could_ go it alone and [listen](/tools/bolt-python/concepts/event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events in order to implement the AI features in your app. That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! +::: + +While the `assistant_thread_started` and `assistant_thread_context_changed` events do provide Slack-client thread context information, the `message.im` event does not. Any subsequent user message events won't contain thread context data. For that reason, Bolt not only provides a way to store thread context — the `threadContextStore` property — but it also provides a `DefaultThreadContextStore` instance that is utilized by default. This implementation relies on storing and retrieving [message metadata](/messaging/message-metadata/) as the user interacts with the app. -## The `Assistant` class instance {#assistant-class} +If you do provide your own `threadContextStore` property, it must feature `get` and `save` methods. + +:::tip[Refer to the [reference docs](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments.] +::: + +### Configuring your app to support the `Assistant` class {#configuring-assistant-class} + +1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & AI Apps** feature. + +2. Within the App Settings **OAuth & Permissions** page, add the following scopes: + * [`assistant:write`](/reference/scopes/assistant.write) + * [`chat:write`](/reference/scopes/chat.write) + * [`im:history`](/reference/scopes/im.history) -The `Assistant` class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. A typical flow would look like: +3. Within the App Settings **Event Subscriptions** page, subscribe to the following events: + * [`assistant_thread_started`](/reference/events/assistant_thread_started) + * [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) + * [`message.im`](/reference/events/message.im) -1. [The user starts a thread](#handling-a-new-thread). The `Assistant` class handles the incoming [`assistant_thread_started`](/reference/events/assistant_thread_started) event. -2. [The thread context may change at any point](#handling-thread-context-changes). The `Assistant` class can handle any incoming [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) events. The class also provides a default context store to keep track of thread context changes as the user moves through Slack. -3. [The user responds](#handling-the-user-response). The `Assistant` class handles the incoming [`message.im`](/reference/events/message.im) event. +### Handling a new thread {#handling-new-thread} +When the user opens a new thread with your AI-enabled app, the [`assistant_thread_started`](/reference/events/assistant_thread_started) event will be sent to your app. + +:::tip[When a user opens an app thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data.] + +You can grab that info by using the `get_thread_context` utility, as subsequent user message event payloads won't include the channel info. +::: ```python assistant = Assistant() -# This listener is invoked when a human user opened an assistant thread @assistant.thread_started -def start_assistant_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts): - # Send the first reply to the human who started chat with your app's assistant bot - say(":wave: Hi, how can I help you today?") - - # Setting suggested prompts is optional - set_suggested_prompts( - prompts=[ - # If the suggested prompt is long, you can use {"title": "short one to display", "message": "full prompt"} instead - "What does SLACK stand for?", - "When Slack was released?", - ], - ) +def start_assistant_thread( + say: Say, + get_thread_context: GetThreadContext, + set_suggested_prompts: SetSuggestedPrompts, + logger: logging.Logger, +): + try: + say("How can I help you?") + + prompts: List[Dict[str, str]] = [ + { + "title": "Suggest names for my Slack app", + "message": "Can you suggest a few names for my Slack app? The app helps my teammates better organize information and plan priorities and action items.", + }, + ] + + thread_context = get_thread_context() + if thread_context is not None and thread_context.channel_id is not None: + summarize_channel = { + "title": "Summarize the referred channel", + "message": "Can you generate a brief summary of the referred channel?", + } + prompts.append(summarize_channel) + + set_suggested_prompts(prompts=prompts) + except Exception as e: + logger.exception(f"Failed to handle an assistant_thread_started event: {e}", e) + say(f":warning: Something went wrong! ({e})") +``` + +You can send more complex messages to the user — see [Sending Block Kit alongside messages](#block-kit-interactions) for more info. + +### Handling thread context changes {#handling-thread-context-changes} + +When the user switches channels, the [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) event will be sent to your app. + +If you use the built-in `Assistant` middleware without any custom configuration, the updated context data is automatically saved as [message metadata](/messaging/message-metadata/) of the first reply from the app. + +As long as you use the built-in approach, you don't need to store the context data within a datastore. The downside of this default behavior is the overhead of additional calls to the Slack API. These calls include those to `conversations.history`, which are used to look up the stored message metadata that contains the thread context (via `get_thread_context`). + +To store context elsewhere, pass a custom `AssistantThreadContextStore` implementation to the `Assistant` constructor. We provide `FileAssistantThreadContextStore`, which is a reference implementation that uses the local file system. Since this reference implementation relies on local files, it's not advised for use in production. For production apps, we recommend creating a class that inherits `AssistantThreadContextStore`. + +```python +from slack_bolt import FileAssistantThreadContextStore +assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) +``` + +### Handling the user response {#handling-user-response} + +When the user messages your app, the [`message.im`](/reference/events/message.im) event will be sent to your app. +Messages sent to the app do not contain a [subtype](/reference/events/message#subtypes) and must be deduced based on their shape and any provided [message metadata](/messaging/message-metadata/). + +There are three utilities that are particularly useful in curating the user experience: +* [`say`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.Say) +* [`setTitle`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetTitle) +* [`setStatus`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetStatus) + +Within the `setStatus` utility, you can cycle through strings passed into a `loading_messages` array. + +```python # This listener is invoked when the human user sends a reply in the assistant thread @assistant.user_message def respond_in_assistant_thread( - payload: dict, - logger: logging.Logger, - context: BoltContext, - set_status: SetStatus, client: WebClient, + context: BoltContext, + get_thread_context: GetThreadContext, + logger: logging.Logger, + payload: dict, say: Say, + set_status: SetStatus, ): try: - # Tell the human user the assistant bot acknowledges the request and is working on it - set_status("is typing...") + channel_id = payload["channel"] + team_id = payload["team"] + thread_ts = payload["thread_ts"] + user_id = payload["user"] + user_message = payload["text"] + + set_status( + status="thinking...", + loading_messages=[ + "Untangling the internet cables…", + "Consulting the office goldfish…", + "Convincing the AI to stop overthinking…", + ], + ) # Collect the conversation history with this user - replies_in_thread = client.conversations_replies( + replies = client.conversations_replies( channel=context.channel_id, ts=context.thread_ts, oldest=context.thread_ts, limit=10, ) messages_in_thread: List[Dict[str, str]] = [] - for message in replies_in_thread["messages"]: + for message in replies["messages"]: role = "user" if message.get("bot_id") is None else "assistant" messages_in_thread.append({"role": role, "content": message["text"]}) - # Pass the latest prompt and chat history to the LLM (call_llm is your own code) returned_message = call_llm(messages_in_thread) # Post the result in the assistant thread @@ -93,23 +205,7 @@ def respond_in_assistant_thread( app.use(assistant) ``` -While the `assistant_thread_started` and `assistant_thread_context_changed` events do provide Slack-client thread context information, the `message.im` event does not. Any subsequent user message events won't contain thread context data. For that reason, Bolt not only provides a way to store thread context — the `threadContextStore` property — but it also provides an instance that is utilized by default. This implementation relies on storing and retrieving [message metadata](/messaging/message-metadata/) as the user interacts with the app. - -If you do provide your own `threadContextStore` property, it must feature `get` and `save` methods. - -:::tip[Refer to the [module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments.] -::: - -## Handling a new thread {#handling-a-new-thread} - -When the user opens a new thread with your AI-enabled app, the [`assistant_thread_started`](/reference/events/assistant_thread_started) event will be sent to your app. - -:::tip[When a user opens an app thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data.] - -You can grab that info by using the `get_thread_context` utility, as subsequent user message event payloads won't include the channel info. -::: - -### Block Kit interactions in the app thread {#block-kit-interactions} +### Sending Block Kit alongside messages {#block-kit-interactions} For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](/messaging/message-metadata/) to trigger subsequent interactions with the user. @@ -235,52 +331,182 @@ def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: ... ``` -## Handling thread context changes {#handling-thread-context-changes} +See the [_Adding and handling feedback_](#adding-and-handling-feedback) section for adding feedback buttons with Block Kit. -When the user switches channels, the [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) event will be sent to your app. +## Text streaming in messages {#text-streaming} -If you use the built-in `Assistant` middleware without any custom configuration, the updated context data is automatically saved as [message metadata](/messaging/message-metadata/) of the first reply from the app. +Three Web API methods work together to provide users a text streaming experience: -As long as you use the built-in approach, you don't need to store the context data within a datastore. The downside of this default behavior is the overhead of additional calls to the Slack API. These calls include those to `conversations.history`, which are used to look up the stored message metadata that contains the thread context (via `get_thread_context`). +* the [`chat.startStream`](/reference/methods/chat.startstream) method starts the text stream, +* the [`chat.appendStream`](/reference/methods/chat.appendstream) method appends text to the stream, and +* the [`chat.stopStream`](/reference/methods/chat.stopstream) method stops it. + +Since you're using Bolt for Python, built upon the Python Slack SDK, you can use the [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) utility to streamline all three aspects of streaming in your app's messages. + +The following example uses OpenAI's streaming API with the new `chat_stream()` functionality, but you can substitute it with the AI client of your choice. -To store context elsewhere, pass a custom `AssistantThreadContextStore` implementation to the `Assistant` constructor. We provide `FileAssistantThreadContextStore`, which is a reference implementation that uses the local file system. Since this reference implementation relies on local files, it's not advised for use in production. For production apps, we recommend creating a class that inherits `AssistantThreadContextStore`. ```python -from slack_bolt import FileAssistantThreadContextStore -assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) -``` +import os +from typing import List, Dict + +import openai +from openai import Stream +from openai.types.responses import ResponseStreamEvent + +DEFAULT_SYSTEM_CONTENT = """ +You're an assistant in a Slack workspace. +Users in the workspace will ask you to help them write something or to think better about a specific topic. +You'll respond to those questions in a professional way. +When you include markdown text, convert them to Slack compatible ones. +When a prompt has Slack's special syntax like <@USER_ID> or <#CHANNEL_ID>, you must keep them as-is in your response. +""" + +def call_llm( + messages_in_thread: List[Dict[str, str]], + system_content: str = DEFAULT_SYSTEM_CONTENT, +) -> Stream[ResponseStreamEvent]: + openai_client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) + messages = [{"role": "system", "content": system_content}] + messages.extend(messages_in_thread) + response = openai_client.responses.create(model="gpt-4o-mini", input=messages, stream=True) + return response + +@assistant.user_message +def respond_in_assistant_thread( + ... +): + try: + ... + replies = client.conversations_replies( + channel=context.channel_id, + ts=context.thread_ts, + oldest=context.thread_ts, + limit=10, + ) + messages_in_thread: List[Dict[str, str]] = [] + for message in replies["messages"]: + role = "user" if message.get("bot_id") is None else "assistant" + messages_in_thread.append({"role": role, "content": message["text"]}) -## Handling the user response {#handling-the-user-response} + returned_message = call_llm(messages_in_thread) -When the user messages your app, the [`message.im`](/reference/events/message.im) event will be sent to your app. + streamer = client.chat_stream( + channel=channel_id, + recipient_team_id=team_id, + recipient_user_id=user_id, + thread_ts=thread_ts, + ) -Messages sent to the app do not contain a [subtype](/reference/events/message#subtypes) and must be deduced based on their shape and any provided [message metadata](/messaging/message-metadata/). + # Loop over OpenAI response stream + # https://platform.openai.com/docs/api-reference/responses/create + for event in returned_message: + if event.type == "response.output_text.delta": + streamer.append(markdown_text=f"{event.delta}") + else: + continue -There are three utilities that are particularly useful in curating the user experience: -* [`say`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.Say) -* [`setTitle`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetTitle) -* [`setStatus`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetStatus) + streamer.stop() -```python -... -# This listener is invoked when the human user posts a reply -@assistant.user_message -def respond_to_user_messages(logger: logging.Logger, set_status: SetStatus, say: Say): - try: - set_status("is typing...") - say("Please use the buttons in the first reply instead :bow:") except Exception as e: - logger.exception(f"Failed to respond to an inquiry: {e}") - say(f":warning: Sorry, something went wrong during processing your request (error: {e})") + logger.exception(f"Failed to handle a user message event: {e}") + say(f":warning: Something went wrong! ({e})") +``` -# Enable this assistant middleware in your Bolt app -app.use(assistant) +## Adding and handling feedback {#adding-and-handling-feedback} + +Use the [feedback buttons block element](/reference/block-kit/block-elements/feedback-buttons-element/) to allow users to immediately provide feedback regarding your app's responses. Here's a quick example: + +```py +from typing import List +from slack_sdk.models.blocks import Block, ContextActionsBlock, FeedbackButtonsElement, FeedbackButtonObject + + +def create_feedback_block() -> List[Block]: + """ + Create feedback block with thumbs up/down buttons + + Returns: + Block Kit context_actions block + """ + blocks: List[Block] = [ + ContextActionsBlock( + elements=[ + FeedbackButtonsElement( + action_id="feedback", + positive_button=FeedbackButtonObject( + text="Good Response", + accessibility_label="Submit positive feedback on this response", + value="good-feedback", + ), + negative_button=FeedbackButtonObject( + text="Bad Response", + accessibility_label="Submit negative feedback on this response", + value="bad-feedback", + ), + ) + ] + ) + ] + return blocks +``` + +Use the `chat_stream` utility to render the feedback block at the bottom of your app's message. + +```js +... + streamer = client.chat_stream( + channel=channel_id, + recipient_team_id=team_id, + recipient_user_id=user_id, + thread_ts=thread_ts, + ) + + # Loop over OpenAI response stream + # https://platform.openai.com/docs/api-reference/responses/create + for event in returned_message: + if event.type == "response.output_text.delta": + streamer.append(markdown_text=f"{event.delta}") + else: + continue + + feedback_block = create_feedback_block() + streamer.stop(blocks=feedback_block) +... ``` -## Full example: Assistant Template {#full-example} +Then add a response for when the user provides feedback. + +```python +# Handle feedback buttons (thumbs up/down) +def handle_feedback(ack, body, client, logger: logging.Logger): + try: + ack() + message_ts = body["message"]["ts"] + channel_id = body["channel"]["id"] + feedback_type = body["actions"][0]["value"] + is_positive = feedback_type == "good-feedback" + + if is_positive: + client.chat_postEphemeral( + channel=channel_id, + user=body["user"]["id"], + thread_ts=message_ts, + text="We're glad you found this useful.", + ) + else: + client.chat_postEphemeral( + channel=channel_id, + user=body["user"]["id"], + thread_ts=message_ts, + text="Sorry to hear that response wasn't up to par :slightly_frowning_face: Starting a new chat may help with AI mistakes and hallucinations.", + ) + + logger.debug(f"Handled feedback: type={feedback_type}, message_ts={message_ts}") + except Exception as error: + logger.error(f":warning: Something went wrong! {error}") +``` -Below is the `assistant.py` listener file of the [Assistant Template repo](https://github.com/slack-samples/bolt-python-assistant-template) we've created for you to build off of. +## Full example: App Agent Template {#app-agent-template} -```py reference title="assistant.py" -https://github.com/slack-samples/bolt-python-assistant-template/blob/main/listeners/assistant.py -``` \ No newline at end of file +Want to see the functionality described throughout this guide in action? We've created a [App Agent Template](https://github.com/slack-samples/bolt-python-assistant-template) repo for you to build off of. diff --git a/docs/english/concepts/message-sending.md b/docs/english/concepts/message-sending.md index 228a7b6b8..9741bb396 100644 --- a/docs/english/concepts/message-sending.md +++ b/docs/english/concepts/message-sending.md @@ -5,6 +5,7 @@ Within your listener function, `say()` is available whenever there is an associa In the case that you'd like to send a message outside of a listener or you want to do something more advanced (like handle specific errors), you can call `client.chat_postMessage` [using the client attached to your Bolt instance](/tools/bolt-python/concepts/web-api). Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. + ```python # Listens for messages containing "knock knock" and responds with an italicized "who's there?" @app.message("knock knock") @@ -38,4 +39,62 @@ def show_datepicker(event, say): blocks=blocks, text="Pick a date for me to remind you" ) -``` \ No newline at end of file +``` + +## Streaming messages {#streaming-messages} + +You can have your app's messages stream in to replicate conventional AI chatbot behavior. This is done through three Web API methods: + +* [`chat_startStream`](/reference/methods/chat.startstream) +* [`chat_appendStream`](/reference/methods/chat.appendstream) +* [`chat_stopStream`](/reference/methods/chat.stopstream) + +The Python Slack SDK provides a [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility to streamline calling these methods. Here's an excerpt from our [Assistant template app](https://github.com/slack-samples/bolt-python-assistant-template): + +```python +streamer = client.chat_stream( + channel=channel_id, + recipient_team_id=team_id, + recipient_user_id=user_id, + thread_ts=thread_ts, +) + +# Loop over OpenAI response stream +# https://platform.openai.com/docs/api-reference/responses/create +for event in returned_message: + if event.type == "response.output_text.delta": + streamer.append(markdown_text=f"{event.delta}") + else: + continue + +feedback_block = create_feedback_block() +streamer.stop(blocks=feedback_block) +``` + +In that example, a [feedback buttons](/reference/block-kit/block-elements/feedback-buttons-element) block element is passed to `streamer.stop` to provide feedback buttons to the user at the bottom of the message. Interaction with these buttons will send a block action event to your app to receive the feedback. + +```python +def create_feedback_block() -> List[Block]: + blocks: List[Block] = [ + ContextActionsBlock( + elements=[ + FeedbackButtonsElement( + action_id="feedback", + positive_button=FeedbackButtonObject( + text="Good Response", + accessibility_label="Submit positive feedback on this response", + value="good-feedback", + ), + negative_button=FeedbackButtonObject( + text="Bad Response", + accessibility_label="Submit negative feedback on this response", + value="bad-feedback", + ), + ) + ] + ) + ] + return blocks +``` + +For information on calling the `chat_*Stream` API methods without the helper utility, see the [_Sending streaming messages_](/tools/python-slack-sdk/web#sending-streaming-messages) section of the Python Slack SDK docs. \ No newline at end of file diff --git a/docs/reference/app/app.html b/docs/reference/app/app.html index 3ee02b07c..c91d020ef 100644 --- a/docs/reference/app/app.html +++ b/docs/reference/app/app.html @@ -1195,7 +1195,9 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3018,7 +3020,9 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3028,7 +3032,8 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for +details.

    diff --git a/docs/reference/app/async_app.html b/docs/reference/app/async_app.html index 78959986b..9cbc801d0 100644 --- a/docs/reference/app/async_app.html +++ b/docs/reference/app/async_app.html @@ -1215,7 +1215,9 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3075,7 +3077,9 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3085,7 +3089,8 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for +details.

    def web_app(self, path: str = '/slack/events', port: int = 3000) ‑> aiohttp.web_app.Application diff --git a/docs/reference/app/index.html b/docs/reference/app/index.html index a46bc2e71..8821e5af9 100644 --- a/docs/reference/app/index.html +++ b/docs/reference/app/index.html @@ -1214,7 +1214,9 @@

    Classes

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3037,7 +3039,9 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3047,7 +3051,8 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for +details.

    diff --git a/docs/reference/async_app.html b/docs/reference/async_app.html index 707bfc3dd..8fd975be9 100644 --- a/docs/reference/async_app.html +++ b/docs/reference/async_app.html @@ -1306,7 +1306,9 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3166,7 +3168,9 @@

    Args

    middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]] = None, ) -> Callable[..., Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3176,7 +3180,8 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for +details.

    def web_app(self, path: str = '/slack/events', port: int = 3000) ‑> aiohttp.web_app.Application @@ -5158,6 +5163,7 @@

    Class variables

    icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, + markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full @@ -5183,6 +5189,7 @@

    Class variables

    icon_emoji=icon_emoji, icon_url=icon_url, username=username, + markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, @@ -5248,11 +5255,18 @@

    Class variables

    self.channel_id = channel_id self.thread_ts = thread_ts - async def __call__(self, status: str) -> AsyncSlackResponse: + async def __call__( + self, + status: str, + loading_messages: Optional[List[str]] = None, + **kwargs, + ) -> AsyncSlackResponse: return await self.client.assistant_threads_setStatus( - status=status, channel_id=self.channel_id, thread_ts=self.thread_ts, + status=status, + loading_messages=loading_messages, + **kwargs, )
    @@ -5298,7 +5312,7 @@

    Class variables

    async def __call__( self, - prompts: List[Union[str, Dict[str, str]]], + prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] diff --git a/docs/reference/context/say/async_say.html b/docs/reference/context/say/async_say.html index 8547a1188..e170251fe 100644 --- a/docs/reference/context/say/async_say.html +++ b/docs/reference/context/say/async_say.html @@ -87,6 +87,7 @@

    Classes

    icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, + markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full @@ -112,6 +113,7 @@

    Classes

    icon_emoji=icon_emoji, icon_url=icon_url, username=username, + markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, diff --git a/docs/reference/context/say/index.html b/docs/reference/context/say/index.html index 7a5850760..e2ed0d03f 100644 --- a/docs/reference/context/say/index.html +++ b/docs/reference/context/say/index.html @@ -105,6 +105,7 @@

    Classes

    icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, + markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full @@ -130,6 +131,7 @@

    Classes

    icon_emoji=icon_emoji, icon_url=icon_url, username=username, + markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, diff --git a/docs/reference/context/say/say.html b/docs/reference/context/say/say.html index 5db4f24ba..c66e2776f 100644 --- a/docs/reference/context/say/say.html +++ b/docs/reference/context/say/say.html @@ -90,6 +90,7 @@

    Classes

    icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, + markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full @@ -115,6 +116,7 @@

    Classes

    icon_emoji=icon_emoji, icon_url=icon_url, username=username, + markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, diff --git a/docs/reference/context/set_status/async_set_status.html b/docs/reference/context/set_status/async_set_status.html index 6a15d70ae..06efd6447 100644 --- a/docs/reference/context/set_status/async_set_status.html +++ b/docs/reference/context/set_status/async_set_status.html @@ -70,11 +70,18 @@

    Classes

    self.channel_id = channel_id self.thread_ts = thread_ts - async def __call__(self, status: str) -> AsyncSlackResponse: + async def __call__( + self, + status: str, + loading_messages: Optional[List[str]] = None, + **kwargs, + ) -> AsyncSlackResponse: return await self.client.assistant_threads_setStatus( - status=status, channel_id=self.channel_id, thread_ts=self.thread_ts, + status=status, + loading_messages=loading_messages, + **kwargs, )
    diff --git a/docs/reference/context/set_status/index.html b/docs/reference/context/set_status/index.html index 9e53da9a5..aa11815e3 100644 --- a/docs/reference/context/set_status/index.html +++ b/docs/reference/context/set_status/index.html @@ -81,11 +81,18 @@

    Classes

    self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, status: str) -> SlackResponse: + def __call__( + self, + status: str, + loading_messages: Optional[List[str]] = None, + **kwargs, + ) -> SlackResponse: return self.client.assistant_threads_setStatus( - status=status, channel_id=self.channel_id, thread_ts=self.thread_ts, + status=status, + loading_messages=loading_messages, + **kwargs, )
    diff --git a/docs/reference/context/set_status/set_status.html b/docs/reference/context/set_status/set_status.html index 0ec8df5da..e4d839f64 100644 --- a/docs/reference/context/set_status/set_status.html +++ b/docs/reference/context/set_status/set_status.html @@ -70,11 +70,18 @@

    Classes

    self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, status: str) -> SlackResponse: + def __call__( + self, + status: str, + loading_messages: Optional[List[str]] = None, + **kwargs, + ) -> SlackResponse: return self.client.assistant_threads_setStatus( - status=status, channel_id=self.channel_id, thread_ts=self.thread_ts, + status=status, + loading_messages=loading_messages, + **kwargs, )
    diff --git a/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html index 449a72117..4feda52ba 100644 --- a/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html +++ b/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html @@ -72,7 +72,7 @@

    Classes

    async def __call__( self, - prompts: List[Union[str, Dict[str, str]]], + prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] diff --git a/docs/reference/context/set_suggested_prompts/index.html b/docs/reference/context/set_suggested_prompts/index.html index ee5371cea..12d864dde 100644 --- a/docs/reference/context/set_suggested_prompts/index.html +++ b/docs/reference/context/set_suggested_prompts/index.html @@ -83,7 +83,7 @@

    Classes

    def __call__( self, - prompts: List[Union[str, Dict[str, str]]], + prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] diff --git a/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html index 133d3a55a..6c0385e57 100644 --- a/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html +++ b/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html @@ -72,7 +72,7 @@

    Classes

    def __call__( self, - prompts: List[Union[str, Dict[str, str]]], + prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] diff --git a/docs/reference/index.html b/docs/reference/index.html index 1ce8cd134..7bd6d117e 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -1335,7 +1335,9 @@

    Class variables

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3158,7 +3160,9 @@

    Args

    middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. - Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.""" + Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for + details. + """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) @@ -3168,7 +3172,8 @@

    Args

    return __call__

    Registers a new view_submission listener. -Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details.

    +Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for +details.

    @@ -5691,6 +5696,7 @@

    Class variables

    icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, + markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full @@ -5716,6 +5722,7 @@

    Class variables

    icon_emoji=icon_emoji, icon_url=icon_url, username=username, + markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, @@ -5786,11 +5793,18 @@

    Class variables

    self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, status: str) -> SlackResponse: + def __call__( + self, + status: str, + loading_messages: Optional[List[str]] = None, + **kwargs, + ) -> SlackResponse: return self.client.assistant_threads_setStatus( - status=status, channel_id=self.channel_id, thread_ts=self.thread_ts, + status=status, + loading_messages=loading_messages, + **kwargs, )
    @@ -5836,7 +5850,7 @@

    Class variables

    def __call__( self, - prompts: List[Union[str, Dict[str, str]]], + prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] diff --git a/pyproject.toml b/pyproject.toml index 5a6523f35..5361ef1b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ "Operating System :: OS Independent", ] requires-python = ">=3.7" -dependencies = ["slack_sdk>=3.35.0,<4"] +dependencies = ["slack_sdk>=3.37.0,<4"] [project.urls] diff --git a/slack_bolt/context/say/async_say.py b/slack_bolt/context/say/async_say.py index b771529b0..c492e5d77 100644 --- a/slack_bolt/context/say/async_say.py +++ b/slack_bolt/context/say/async_say.py @@ -1,14 +1,14 @@ -from typing import Optional, Union, Dict, Sequence, Callable, Awaitable +from typing import Awaitable, Callable, Dict, Optional, Sequence, Union -from slack_sdk.models.metadata import Metadata - -from slack_bolt.context.say.internals import _can_say -from slack_bolt.util.utils import create_copy from slack_sdk.models.attachments import Attachment from slack_sdk.models.blocks import Block +from slack_sdk.models.metadata import Metadata from slack_sdk.web.async_client import AsyncWebClient from slack_sdk.web.async_slack_response import AsyncSlackResponse +from slack_bolt.context.say.internals import _can_say +from slack_bolt.util.utils import create_copy + class AsyncSay: client: Optional[AsyncWebClient] @@ -42,6 +42,7 @@ async def __call__( icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, + markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full @@ -67,6 +68,7 @@ async def __call__( icon_emoji=icon_emoji, icon_url=icon_url, username=username, + markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, diff --git a/slack_bolt/context/say/say.py b/slack_bolt/context/say/say.py index 6cfbcd801..a6e5904e3 100644 --- a/slack_bolt/context/say/say.py +++ b/slack_bolt/context/say/say.py @@ -1,4 +1,4 @@ -from typing import Optional, Union, Dict, Sequence, Callable +from typing import Callable, Dict, Optional, Sequence, Union from slack_sdk import WebClient from slack_sdk.models.attachments import Attachment @@ -45,6 +45,7 @@ def __call__( icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, + markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full @@ -70,6 +71,7 @@ def __call__( icon_emoji=icon_emoji, icon_url=icon_url, username=username, + markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, diff --git a/slack_bolt/context/set_status/async_set_status.py b/slack_bolt/context/set_status/async_set_status.py index 926ec6de8..e2c451f46 100644 --- a/slack_bolt/context/set_status/async_set_status.py +++ b/slack_bolt/context/set_status/async_set_status.py @@ -1,3 +1,5 @@ +from typing import List, Optional + from slack_sdk.web.async_client import AsyncWebClient from slack_sdk.web.async_slack_response import AsyncSlackResponse @@ -17,9 +19,16 @@ def __init__( self.channel_id = channel_id self.thread_ts = thread_ts - async def __call__(self, status: str) -> AsyncSlackResponse: + async def __call__( + self, + status: str, + loading_messages: Optional[List[str]] = None, + **kwargs, + ) -> AsyncSlackResponse: return await self.client.assistant_threads_setStatus( - status=status, channel_id=self.channel_id, thread_ts=self.thread_ts, + status=status, + loading_messages=loading_messages, + **kwargs, ) diff --git a/slack_bolt/context/set_status/set_status.py b/slack_bolt/context/set_status/set_status.py index 8df0d49a7..0ed612e16 100644 --- a/slack_bolt/context/set_status/set_status.py +++ b/slack_bolt/context/set_status/set_status.py @@ -1,3 +1,5 @@ +from typing import List, Optional + from slack_sdk import WebClient from slack_sdk.web import SlackResponse @@ -17,9 +19,16 @@ def __init__( self.channel_id = channel_id self.thread_ts = thread_ts - def __call__(self, status: str) -> SlackResponse: + def __call__( + self, + status: str, + loading_messages: Optional[List[str]] = None, + **kwargs, + ) -> SlackResponse: return self.client.assistant_threads_setStatus( - status=status, channel_id=self.channel_id, thread_ts=self.thread_ts, + status=status, + loading_messages=loading_messages, + **kwargs, ) diff --git a/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py index aeeb244d7..2079b6448 100644 --- a/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py +++ b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py @@ -1,4 +1,4 @@ -from typing import List, Dict, Union, Optional +from typing import Dict, List, Optional, Sequence, Union from slack_sdk.web.async_client import AsyncWebClient from slack_sdk.web.async_slack_response import AsyncSlackResponse @@ -21,7 +21,7 @@ def __init__( async def __call__( self, - prompts: List[Union[str, Dict[str, str]]], + prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] diff --git a/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py index fc9304b17..21ff815e1 100644 --- a/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py +++ b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py @@ -1,4 +1,4 @@ -from typing import List, Dict, Union, Optional +from typing import Dict, List, Optional, Sequence, Union from slack_sdk import WebClient from slack_sdk.web import SlackResponse @@ -21,7 +21,7 @@ def __init__( def __call__( self, - prompts: List[Union[str, Dict[str, str]]], + prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] diff --git a/tests/slack_bolt/context/test_say.py b/tests/slack_bolt/context/test_say.py index 9e465e5d5..6ca1fc96a 100644 --- a/tests/slack_bolt/context/test_say.py +++ b/tests/slack_bolt/context/test_say.py @@ -3,10 +3,7 @@ from slack_sdk.web import SlackResponse from slack_bolt import Say -from tests.mock_web_api_server import ( - setup_mock_web_api_server, - cleanup_mock_web_api_server, -) +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server class TestSay: @@ -24,6 +21,11 @@ def test_say(self): response: SlackResponse = say(text="Hi there!") assert response.status_code == 200 + def test_say_markdown_text(self): + say = Say(client=self.web_client, channel="C111") + response: SlackResponse = say(markdown_text="**Greetings!**") + assert response.status_code == 200 + def test_say_unfurl_options(self): say = Say(client=self.web_client, channel="C111") response: SlackResponse = say(text="Hi there!", unfurl_media=True, unfurl_links=True) diff --git a/tests/slack_bolt/context/test_set_status.py b/tests/slack_bolt/context/test_set_status.py new file mode 100644 index 000000000..fe998df5e --- /dev/null +++ b/tests/slack_bolt/context/test_set_status.py @@ -0,0 +1,38 @@ +import pytest +from slack_sdk import WebClient +from slack_sdk.web import SlackResponse + +from slack_bolt.context.set_status import SetStatus +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server + + +class TestSetStatus: + def setup_method(self): + setup_mock_web_api_server(self) + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + + def test_set_status(self): + set_status = SetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: SlackResponse = set_status("Thinking...") + assert response.status_code == 200 + + def test_set_status_loading_messages(self): + set_status = SetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: SlackResponse = set_status( + status="Thinking...", + loading_messages=[ + "Sitting...", + "Waiting...", + ], + ) + assert response.status_code == 200 + + def test_set_status_invalid(self): + set_status = SetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + with pytest.raises(TypeError): + set_status() diff --git a/tests/slack_bolt/context/test_set_suggested_prompts.py b/tests/slack_bolt/context/test_set_suggested_prompts.py new file mode 100644 index 000000000..792b974b5 --- /dev/null +++ b/tests/slack_bolt/context/test_set_suggested_prompts.py @@ -0,0 +1,37 @@ +import pytest +from slack_sdk import WebClient +from slack_sdk.web import SlackResponse + +from slack_bolt.context.set_suggested_prompts import SetSuggestedPrompts +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server + + +class TestSetSuggestedPrompts: + def setup_method(self): + setup_mock_web_api_server(self) + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + + def test_set_suggested_prompts(self): + set_suggested_prompts = SetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: SlackResponse = set_suggested_prompts(prompts=["One", "Two"]) + assert response.status_code == 200 + + def test_set_suggested_prompts_objects(self): + set_suggested_prompts = SetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: SlackResponse = set_suggested_prompts( + prompts=[ + "One", + {"title": "Two", "message": "What's before addition?"}, + ], + ) + assert response.status_code == 200 + + def test_set_suggested_prompts_invalid(self): + set_suggested_prompts = SetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + with pytest.raises(TypeError): + set_suggested_prompts() diff --git a/tests/slack_bolt_async/context/test_async_say.py b/tests/slack_bolt_async/context/test_async_say.py index 77ac0cc0e..efa90febc 100644 --- a/tests/slack_bolt_async/context/test_async_say.py +++ b/tests/slack_bolt_async/context/test_async_say.py @@ -2,12 +2,9 @@ from slack_sdk.web.async_client import AsyncWebClient from slack_sdk.web.async_slack_response import AsyncSlackResponse -from tests.utils import get_event_loop from slack_bolt.context.say.async_say import AsyncSay -from tests.mock_web_api_server import ( - cleanup_mock_web_api_server_async, - setup_mock_web_api_server_async, -) +from tests.mock_web_api_server import cleanup_mock_web_api_server_async, setup_mock_web_api_server_async +from tests.utils import get_event_loop class TestAsyncSay: @@ -29,6 +26,12 @@ async def test_say(self): response: AsyncSlackResponse = await say(text="Hi there!") assert response.status_code == 200 + @pytest.mark.asyncio + async def test_say_markdown_text(self): + say = AsyncSay(client=self.web_client, channel="C111") + response: AsyncSlackResponse = await say(markdown_text="**Greetings!**") + assert response.status_code == 200 + @pytest.mark.asyncio async def test_say_unfurl_options(self): say = AsyncSay(client=self.web_client, channel="C111") diff --git a/tests/slack_bolt_async/context/test_async_set_status.py b/tests/slack_bolt_async/context/test_async_set_status.py new file mode 100644 index 000000000..8df34171f --- /dev/null +++ b/tests/slack_bolt_async/context/test_async_set_status.py @@ -0,0 +1,45 @@ +import pytest +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_slack_response import AsyncSlackResponse + +from slack_bolt.context.set_status.async_set_status import AsyncSetStatus +from tests.mock_web_api_server import cleanup_mock_web_api_server_async, setup_mock_web_api_server_async +from tests.utils import get_event_loop + + +class TestAsyncSetStatus: + @pytest.fixture + def event_loop(self): + setup_mock_web_api_server_async(self) + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + + loop = get_event_loop() + yield loop + loop.close() + cleanup_mock_web_api_server_async(self) + + @pytest.mark.asyncio + async def test_set_status(self): + set_status = AsyncSetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: AsyncSlackResponse = await set_status("Thinking...") + assert response.status_code == 200 + + @pytest.mark.asyncio + async def test_set_status_loading_messages(self): + set_status = AsyncSetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: AsyncSlackResponse = await set_status( + status="Thinking...", + loading_messages=[ + "Sitting...", + "Waiting...", + ], + ) + assert response.status_code == 200 + + @pytest.mark.asyncio + async def test_set_status_invalid(self): + set_status = AsyncSetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + with pytest.raises(TypeError): + await set_status() diff --git a/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py b/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py new file mode 100644 index 000000000..70a24efcb --- /dev/null +++ b/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py @@ -0,0 +1,45 @@ +import asyncio + +import pytest +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_slack_response import AsyncSlackResponse + +from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server + + +class TestAsyncSetSuggestedPrompts: + @pytest.fixture + def event_loop(self): + setup_mock_web_api_server(self) + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + + loop = asyncio.get_event_loop() + yield loop + loop.close() + cleanup_mock_web_api_server(self) + + @pytest.mark.asyncio + async def test_set_suggested_prompts(self): + set_suggested_prompts = AsyncSetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: AsyncSlackResponse = await set_suggested_prompts(prompts=["One", "Two"]) + assert response.status_code == 200 + + @pytest.mark.asyncio + async def test_set_suggested_prompts_objects(self): + set_suggested_prompts = AsyncSetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: AsyncSlackResponse = await set_suggested_prompts( + prompts=[ + "One", + {"title": "Two", "message": "What's before addition?"}, + ], + ) + assert response.status_code == 200 + + @pytest.mark.asyncio + async def test_set_suggested_prompts_invalid(self): + set_suggested_prompts = AsyncSetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + with pytest.raises(TypeError): + await set_suggested_prompts() From 5f6196f1570d348db5916aaafbb3d3c212e374dd Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 6 Oct 2025 16:40:52 -0700 Subject: [PATCH 155/282] version 1.26.0 (#1388) --- slack_bolt/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slack_bolt/version.py b/slack_bolt/version.py index 7f9c19341..8cfd6f900 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.25.0" +__version__ = "1.26.0" From 1d4e86bfc13118a4f44d8afe96d6cbeb0ebab88e Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:44:35 -0500 Subject: [PATCH 156/282] docs: add AI to quickstart (#1389) --- docs/english/building-an-app.md | 6 +++--- docs/english/getting-started.md | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/english/building-an-app.md b/docs/english/building-an-app.md index 301cc52c6..ee0dac967 100644 --- a/docs/english/building-an-app.md +++ b/docs/english/building-an-app.md @@ -475,8 +475,8 @@ Now that you have a basic app up and running, you can start exploring how to mak * Read through the concepts pages to learn about the different methods and features your Bolt app has access to. -* Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. All of the events are listed [on the API docs site](/reference/events). +* Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. View the full events reference docs [here](/reference/events). -* Bolt allows you to [call Web API methods](/tools/bolt-python/concepts/web-api) with the client attached to your app. There are [over 200 methods](/reference/methods) on our API site. +* Bolt allows you to [call Web API methods](/tools/bolt-python/concepts/web-api) with the client attached to your app. There are over 200 methods; view them [here](/reference/methods). -* Learn more about the different token types [on the API docs site](/authentication/tokens). Your app may need different tokens depending on the actions you want it to perform. +* Learn more about the different token types in the [tokens guide](/authentication/tokens). Your app may need different tokens depending on the actions you want it to perform. \ No newline at end of file diff --git a/docs/english/getting-started.md b/docs/english/getting-started.md index 8b7438d65..cc428a93a 100644 --- a/docs/english/getting-started.md +++ b/docs/english/getting-started.md @@ -279,6 +279,41 @@ This will open the following page: On these pages you're free to make changes such as updating your app icon, configuring app features, and perhaps even distributing your app! +## Adding AI features {#ai-features} + +Now that you're familiar with a basic app setup, try it out again, this time using the AI agent template! + + + + +Get started with the agent template: + +```sh +$ slack create ai-app --template slack-samples/bolt-python-assistant-template +$ cd ai-app +``` + + + + +Get started with the agent template: + +```sh +$ git clone https://github.com/slack-samples/bolt-python-assistant-template ai-app +$ cd ai-app +``` + +Using this method, be sure to set the app and bot tokens as we did in the [Running the app](#running-the-app) section above. + + + + +Once the project is created, update the `.env.sample` file by setting the `OPENAI_API_KEY` with the value of your key and removing the `.sample` from the file name. + +In the `ai` folder of this app, you'll find default instructions for the LLM and an OpenAI client setup. + +The `listeners` include utilities intended for messaging with an LLM. Those are outlined in detail in the guide to [Using AI in apps](/tools/bolt-python/concepts/ai-apps) and [Sending messages](/tools/bolt-python/concepts/message-sending). + ## Next steps {#next-steps} Congrats once more on getting up and running with this quick start. From c8a50bee46fde24631a9e1746bdd8c81e5deea05 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 10 Oct 2025 16:08:06 -0400 Subject: [PATCH 157/282] feat: add has_been_called on `complete` & `fail` utility (#1390) --- slack_bolt/context/complete/async_complete.py | 11 +++++++++++ slack_bolt/context/complete/complete.py | 11 +++++++++++ slack_bolt/context/fail/async_fail.py | 11 +++++++++++ slack_bolt/context/fail/fail.py | 11 +++++++++++ tests/scenario_tests/test_function.py | 4 ++++ tests/scenario_tests_async/test_function.py | 4 ++++ tests/slack_bolt/context/test_complete.py | 9 +++++++++ tests/slack_bolt/context/test_fail.py | 9 +++++++++ tests/slack_bolt_async/context/test_async_complete.py | 11 +++++++++++ tests/slack_bolt_async/context/test_async_fail.py | 11 +++++++++++ 10 files changed, 92 insertions(+) diff --git a/slack_bolt/context/complete/async_complete.py b/slack_bolt/context/complete/async_complete.py index fe3d796d1..bb81c2d4a 100644 --- a/slack_bolt/context/complete/async_complete.py +++ b/slack_bolt/context/complete/async_complete.py @@ -7,6 +7,7 @@ class AsyncComplete: client: AsyncWebClient function_execution_id: Optional[str] + _called: bool def __init__( self, @@ -15,6 +16,7 @@ def __init__( ): self.client = client self.function_execution_id = function_execution_id + self._called = False async def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> AsyncSlackResponse: """Signal the successful completion of the custom function. @@ -31,6 +33,15 @@ async def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> AsyncSlack if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") + self._called = True return await self.client.functions_completeSuccess( function_execution_id=self.function_execution_id, outputs=outputs or {} ) + + def has_been_called(self) -> bool: + """Check if this complete function has been called. + + Returns: + bool: True if the complete function has been called, False otherwise. + """ + return self._called diff --git a/slack_bolt/context/complete/complete.py b/slack_bolt/context/complete/complete.py index acba3a412..dc9382384 100644 --- a/slack_bolt/context/complete/complete.py +++ b/slack_bolt/context/complete/complete.py @@ -7,6 +7,7 @@ class Complete: client: WebClient function_execution_id: Optional[str] + _called: bool def __init__( self, @@ -15,6 +16,7 @@ def __init__( ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: """Signal the successful completion of the custom function. @@ -31,4 +33,13 @@ def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") + self._called = True return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {}) + + def has_been_called(self) -> bool: + """Check if this complete function has been called. + + Returns: + bool: True if the complete function has been called, False otherwise. + """ + return self._called diff --git a/slack_bolt/context/fail/async_fail.py b/slack_bolt/context/fail/async_fail.py index 10a39f735..da01067ba 100644 --- a/slack_bolt/context/fail/async_fail.py +++ b/slack_bolt/context/fail/async_fail.py @@ -7,6 +7,7 @@ class AsyncFail: client: AsyncWebClient function_execution_id: Optional[str] + _called: bool def __init__( self, @@ -15,6 +16,7 @@ def __init__( ): self.client = client self.function_execution_id = function_execution_id + self._called = False async def __call__(self, error: str) -> AsyncSlackResponse: """Signal that the custom function failed to complete. @@ -31,4 +33,13 @@ async def __call__(self, error: str) -> AsyncSlackResponse: if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") + self._called = True return await self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) + + def has_been_called(self) -> bool: + """Check if this fail function has been called. + + Returns: + bool: True if the fail function has been called, False otherwise. + """ + return self._called diff --git a/slack_bolt/context/fail/fail.py b/slack_bolt/context/fail/fail.py index 483bcebc3..9b04f6118 100644 --- a/slack_bolt/context/fail/fail.py +++ b/slack_bolt/context/fail/fail.py @@ -7,6 +7,7 @@ class Fail: client: WebClient function_execution_id: Optional[str] + _called: bool def __init__( self, @@ -15,6 +16,7 @@ def __init__( ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, error: str) -> SlackResponse: """Signal that the custom function failed to complete. @@ -31,4 +33,13 @@ def __call__(self, error: str) -> SlackResponse: if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") + self._called = True return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) + + def has_been_called(self) -> bool: + """Check if this fail function has been called. + + Returns: + bool: True if the fail function has been called, False otherwise. + """ + return self._called diff --git a/tests/scenario_tests/test_function.py b/tests/scenario_tests/test_function.py index 0a2152892..5a4fc2685 100644 --- a/tests/scenario_tests/test_function.py +++ b/tests/scenario_tests/test_function.py @@ -300,16 +300,20 @@ def reverse(body, event, context, client, complete, inputs): assert context.client.token == "xwfp-valid" assert client.token == "xwfp-valid" assert complete.client.token == "xwfp-valid" + assert complete.has_been_called() is False complete( outputs={"reverseString": "olleh"}, ) + assert complete.has_been_called() is True def reverse_error(body, event, fail): assert body == function_body assert event == function_body["event"] assert fail.function_execution_id == "Fx111" + assert fail.has_been_called() is False fail(error="there was an error") + assert fail.has_been_called() is True def complete_it(body, event, complete): diff --git a/tests/scenario_tests_async/test_function.py b/tests/scenario_tests_async/test_function.py index 3f8b7a722..142cc1d6c 100644 --- a/tests/scenario_tests_async/test_function.py +++ b/tests/scenario_tests_async/test_function.py @@ -312,18 +312,22 @@ async def reverse(body, event, client, context, complete, inputs): assert context.client.token == "xwfp-valid" assert client.token == "xwfp-valid" assert complete.client.token == "xwfp-valid" + assert complete.has_been_called() is False await complete( outputs={"reverseString": "olleh"}, ) + assert complete.has_been_called() is True async def reverse_error(body, event, fail): assert body == function_body assert event == function_body["event"] assert fail.function_execution_id == "Fx111" + assert fail.has_been_called() is False await fail( error="there was an error", ) + assert fail.has_been_called() is True async def complete_it(body, event, complete): diff --git a/tests/slack_bolt/context/test_complete.py b/tests/slack_bolt/context/test_complete.py index a920c41eb..63a1d9f04 100644 --- a/tests/slack_bolt/context/test_complete.py +++ b/tests/slack_bolt/context/test_complete.py @@ -30,3 +30,12 @@ def test_complete_no_function_execution_id(self): with pytest.raises(ValueError): complete(outputs={"key": "value"}) + + def test_has_been_called_false_initially(self): + complete = Complete(client=self.web_client, function_execution_id="fn1111") + assert complete.has_been_called() is False + + def test_has_been_called_true_after_complete(self): + complete = Complete(client=self.web_client, function_execution_id="fn1111") + complete(outputs={"key": "value"}) + assert complete.has_been_called() is True diff --git a/tests/slack_bolt/context/test_fail.py b/tests/slack_bolt/context/test_fail.py index e4704d376..14348281f 100644 --- a/tests/slack_bolt/context/test_fail.py +++ b/tests/slack_bolt/context/test_fail.py @@ -30,3 +30,12 @@ def test_fail_no_function_execution_id(self): with pytest.raises(ValueError): fail(error="there was an error") + + def test_has_been_called_false_initially(self): + fail = Fail(client=self.web_client, function_execution_id="fn1111") + assert fail.has_been_called() is False + + def test_has_been_called_true_after_fail(self): + fail = Fail(client=self.web_client, function_execution_id="fn1111") + fail(error="there was an error") + assert fail.has_been_called() is True diff --git a/tests/slack_bolt_async/context/test_async_complete.py b/tests/slack_bolt_async/context/test_async_complete.py index f2fd115ec..b2a464f83 100644 --- a/tests/slack_bolt_async/context/test_async_complete.py +++ b/tests/slack_bolt_async/context/test_async_complete.py @@ -36,3 +36,14 @@ async def test_complete_no_function_execution_id(self): with pytest.raises(ValueError): await complete(outputs={"key": "value"}) + + @pytest.mark.asyncio + async def test_has_been_called_false_initially(self): + complete = AsyncComplete(client=self.web_client, function_execution_id="fn1111") + assert complete.has_been_called() is False + + @pytest.mark.asyncio + async def test_has_been_called_true_after_complete(self): + complete = AsyncComplete(client=self.web_client, function_execution_id="fn1111") + await complete(outputs={"key": "value"}) + assert complete.has_been_called() is True diff --git a/tests/slack_bolt_async/context/test_async_fail.py b/tests/slack_bolt_async/context/test_async_fail.py index 854bc7521..d4708927f 100644 --- a/tests/slack_bolt_async/context/test_async_fail.py +++ b/tests/slack_bolt_async/context/test_async_fail.py @@ -36,3 +36,14 @@ async def test_fail_no_function_execution_id(self): with pytest.raises(ValueError): await fail(error="there was an error") + + @pytest.mark.asyncio + async def test_has_been_called_false_initially(self): + fail = AsyncFail(client=self.web_client, function_execution_id="fn1111") + assert fail.has_been_called() is False + + @pytest.mark.asyncio + async def test_has_been_called_true_after_fail(self): + fail = AsyncFail(client=self.web_client, function_execution_id="fn1111") + await fail(error="there was an error") + assert fail.has_been_called() is True From ad2da997112bd917eb14d73c582066d7ca24b936 Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:29:26 -0500 Subject: [PATCH 158/282] docs: order confirmation tutorial (#1381) --- docs/english/_sidebar.json | 1 + .../order-confirmation/order-confirmation.md | 553 ++++++++++++++++++ docs/img/delivery-tracker-main.png | Bin 0 -> 65799 bytes 3 files changed, 554 insertions(+) create mode 100644 docs/english/tutorial/order-confirmation/order-confirmation.md create mode 100644 docs/img/delivery-tracker-main.png diff --git a/docs/english/_sidebar.json b/docs/english/_sidebar.json index d42868543..859c4b52f 100644 --- a/docs/english/_sidebar.json +++ b/docs/english/_sidebar.json @@ -96,6 +96,7 @@ "label": "Tutorials", "items": [ "tools/bolt-python/tutorial/ai-chatbot/ai-chatbot", + "tools/bolt-python/tutorial/order-confirmation/order-confirmation", "tools/bolt-python/tutorial/custom-steps", "tools/bolt-python/tutorial/custom-steps-for-jira/custom-steps-for-jira", "tools/bolt-python/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new", diff --git a/docs/english/tutorial/order-confirmation/order-confirmation.md b/docs/english/tutorial/order-confirmation/order-confirmation.md new file mode 100644 index 000000000..695d6965a --- /dev/null +++ b/docs/english/tutorial/order-confirmation/order-confirmation.md @@ -0,0 +1,553 @@ +--- +title: Create a Salesforce order confirmation app +--- + +In this tutorial, you'll use the [Bolt for Python](/tools/bolt-python/) framework and [Block Kit Builder](https://app.slack.com/block-kit-builder) to create an order confirmation app that links to a system of record, like Salesforce. + +The Slack app will: +* allow users to enter order numbers from within Slack, along with some additional order information, +* post that information to a Slack channel, and +* send the information to the system of record. + +End users will be able to enter information across devices, as many will likely be using a mobile device. + +Along the way, you'll learn how to use the Bolt for Python starter app template as a jumping off point for your own custom apps. Let's begin! + +:::warning[Consider the following] + +This tutorial was created for educational purposes within a Slack workshop. As a result, it has not been tested quite as rigorously as our sample apps. Proceed carefully if you'd like to use a similar app in production. + +::: + +## Getting started + +### Installing the Slack CLI + +If you don't already have the Slack CLI, install it from your terminal: navigate to the installation guide ([for Mac and Linux](/tools/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux) or [for Windows](/tools/slack-cli/guides/installing-the-slack-cli-for-windows)) and follow the steps. + +### Cloning the starter app + +Once installed, use the command `slack create` to get started with the Bolt for Python [starter template](https://github.com/slack-samples/bolt-python-starter-template). Alternatively, you can clone the template using Git. + +You can remove the portions from the template that are not used within this tutorial to make things a bit cleaner for yourself. To do this, open your project in VS Code (you can do this from the terminal with the `code .` command) and delete the `commands`, `events`, and `shortcuts` folders from the `/listeners` folder. You can also do the same to the corresponding folders within the `/listeners/tests` folder as well. Finally, remove the imports of these files from the `/listeners/__init__.py` file. + +## Creating your app + +We’ll use the contents of the `manifest.json` file below. This file describes the metadata associated with your app, like its name and permissions that it requests. + +These values are used to create an app in one of two ways: + +- **With the Slack CLI**: Save the contents of the file to your project's `manifest.json` file then skip ahead to [starting your app](#starting-your-app). +- **With app settings**: Copy the contents of the file and [create a new app](https://api.slack.com/apps/new). Next, choose **From a manifest** and follow the prompts, pasting the manifest file contents you copied. + +```json +{ + "_metadata": { + "major_version": 1, + "minor_version": 1 + }, + "display_information": { + "name": "Delivery Tracker App" + }, + "features": { + "bot_user": { + "display_name": "Delivery Tracker App", + "always_online": false + } + }, + "oauth_config": { + "scopes": { + "bot": [ + "channels:history", + "chat:write" + ] + } + }, + "settings": { + "event_subscriptions": { + "bot_events": [ + "message.channels" + ] + }, + "interactivity": { + "is_enabled": true + }, + "org_deploy_enabled": false, + "socket_mode_enabled": true, + "token_rotation_enabled": false + } +} +``` + +### Tokens + +Once your app has been created, scroll down to **App-Level Tokens** on the **Basic Information** page and create a token that requests the [`connections:write`](/reference/scopes/connections.write) scope. This token will allow you to use [Socket Mode](/apis/events-api/using-socket-mode), which is a secure way to develop on Slack through the use of WebSockets. Save the value of your app token and store it in a safe place (we’ll use it in the next step). + +### Install app + +Still in the app settings, navigate to the **Install App** page in the left sidebar. Install your app. When you press **Allow**, this means you’re agreeing to install your app with the permissions that it’s requesting. Copy the bot token that you receive as well and store this in a safe place as well for subsequent steps. + +## Saving credentials + +Within a terminal of your choice, set the two tokens from the previous step as environment variables using the commands below. Make sure not to mix these two up, `SLACK_APP_TOKEN` will start with “xapp-“ and `SLACK_BOT_TOKEN` will start with “xoxb-“. + +For macOS: + +```bash +export SLACK_APP_TOKEN= +export SLACK_BOT_TOKEN= +``` + +For Windows Command Prompt: + +```cmd +set SLACK_APP_TOKEN= +set SLACK_BOT_TOKEN= +``` + +For Windows PowerShell: + +```powershell +$env:SLACK_APP_TOKEN="YOUR-APP-TOKEN-HERE" +$env:SLACK_BOT_TOKEN="YOUR-BOT-TOKEN-HERE" +``` + +## Starting your app {#starting-your-app} + +Run the following commands to activate a virtual environment for your Python packages to be installed, install the dependencies, and start your app. + +```bash +# Setup your python virtual environment +python -m venv .venv +source .venv/bin/activate + +# Install the dependencies +pip install -r requirements.txt + +# Start your local server +slack run +``` + +If you're not using the Slack CLI, a different `python` command can be used to start your app instead: + +```sh +python app.py +``` + +Now that your app is running, you should be able to see it within Slack. In Slack, create a channel that you can test in and try inviting your bot to it using the `/invite @Your-app-name-here` command. Check that your app works by saying “hi” in the channel where your app is, and you should receive a message back from it. If you don’t, ensure you completed all the steps above. + +## Coding the app + +We'll make four changes to the app: + +* Update the “hi” message to something more interesting and interactive +* Handle when the wrong delivery ID button is pressed +* Handle when the correct delivery IDs are sent and bring up a modal for more information +* Send the information to all of the places needed when the form is submitted (including third-party locations) + +For all of these steps, we will use [Block Kit Builder](https://app.slack.com/block-kit-builder), a tool that helps you create messages, modals and other surfaces within Slack. Open [Block Kit Builder](https://app.slack.com/block-kit-builder), take a look, and play around! We’ll create some views next. + +### Updating the "hi" message + +The first thing we want to do is change the “hi, how are you?” message from our app into something more useful. Here’s a `blocks` object built with Block Kit Builder: + +```json + + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Confirm *{delivery_id}* is correct?" + } + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Correct", + "emoji": true + }, + "style": "primary", + "action_id": "approve_delivery" + }, + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Not correct", + "emoji": true + }, + "style": "danger", + "action_id": "deny_delivery" + } + ] + } + ] + +``` + +Take the function below and place your blocks within the blocks dictionary `[]`. + +```python +def delivery_message_callback(context: BoltContext, say: Say, logger: Logger): + try: + delivery_id = context["matches"][0] + say( + blocks=[] # insert your blocks here + ) + except Exception as e: + logger.error(e) +``` + +Update the payload: +* Remove the initial blocks key and convert any boolean true values to `True` to fit with Python conventions. +* If you see variables within `{}` brackets, this is part of an f-string, which allows you to insert variables within strings in a clean manner. Place the `f` character before these strings like this: + +```python +{ + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"Confirm *{delivery_id}* is correct?", # place the "f" character here at the beginning of the string + }, +}, +``` + +Place all of this in the `sample_message.py` file. + +Next, you’ll need to register this listener to respond when a message is sent in the channel with your app. Head to `messages/__init__.py` and overwrite the function there with the one below, which registers the function. Don’t forget to add the import to the callback function as well! + +```python +from .sample_message import delivery_message_callback # import the function to this file + +def register(app: App): + # This regex will capture any number letters followed by dash + # and then any number of digits, our "confirmation number" e.g. ASDF-1234 + app.message(re.compile(r"[A-Za-z]+-\d+"))(delivery_message_callback) ## add this line! +``` + +Now, restart your server to bring in the new code and test that your function works by sending an order confirmation ID, like `HWOA-1524`, in your testing channel. Your app should respond with the message you created within Block Kit Builder. + +### Handling an incorrect delivery ID + +Notice that if you try to click on either of the buttons within your message, nothing will happen. This is because we have yet to create a function to handle the button click. Let’s start with the `Not correct` button first. + +1. Head to Block Kit Builder once again. We want to build a message that lets the user know that the wrong order ID has been submitted. Here's a [section](/reference/block-kit/blocks/section-block) block to get you started: + +```json + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Delivery *{delivery_id}* was incorrect ❌" + } + } + ] +``` + +View this block in Block Kit Builder [here](https://app.slack.com/block-kit-builder/#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Delivery%20*%7Bdelivery_id%7D*%20was%20incorrect%20%E2%9D%8C%22%7D%7D%5D%7D). + +2. Once you have something that you like, add it to the function below and place the function within the `actions/sample_action.py` file. Remember to make any strings with variables into f-strings! + +```python +def deny_delivery_callback(ack, body, client, logger: Logger): + try: + ack() + delivery_id = body["message"]["text"].split("*")[1] + + # Calls the chat.update function to replace the message, + # preventing it from being pressed more than once. + client.chat_update( + channel=body["container"]["channel_id"], + ts=body["container"]["message_ts"], + blocks=[], # Add your blocks here! + ) + + logger.info(f"Delivery denied by user {body['user']['id']}") + except Exception as e: + logger.error(e) +``` + +This function will call the [`chat.update`](/reference/methods/chat.update) Web API method, which will update the original message with buttons, to the one that we created previously. This will also prevent the message from being pressed more than once. + +3. Make the connection to this function again within the `actions/__init__.py` folder with the following code: + +```python +from slack_bolt import App +from .sample_action import sample_action_callback # This can be deleted +from .sample_action import deny_delivery_callback + +def register(app: App): + app.action("sample_action_id")(sample_action_callback) # This can be deleted + app.action("deny_delivery")(deny_delivery_callback) # Add this line +``` + +Test out your app by sending in a confirmation number into your channel and clicking the `Not correct` button. If the message is updated, then you’re good to go onto the next step. + +### Handling a correct delivery ID + +The next step is to handle the `Confirm` button. In this case, we’re going to pull up a modal instead of just a message. + +1. Using the following modal as a base; create a modal that captures the kind of information that you need. + +```json +{ + "title": { + "type": "plain_text", + "text": "Approve Delivery" + }, + "submit": { + "type": "plain_text", + "text": "Approve" + }, + "type": "modal", + "callback_id": "approve_delivery_view", + "private_metadata": "{delivery_id}", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Approving delivery *{delivery_id}*" + } + }, + { + "type": "input", + "block_id": "notes", + "label": { + "type": "plain_text", + "text": "Additional delivery notes" + }, + "element": { + "type": "plain_text_input", + "action_id": "notes_input", + "multiline": true, + "placeholder": { + "type": "plain_text", + "text": "Add notes..." + } + }, + "optional": true + }, + { + "type": "input", + "block_id": "location", + "label": { + "type": "plain_text", + "text": "Delivery Location" + }, + "element": { + "type": "plain_text_input", + "action_id": "location_input", + "placeholder": { + "type": "plain_text", + "text": "Enter the location details..." + } + }, + "optional": true + }, + { + "type": "input", + "block_id": "channel", + "label": { + "type": "plain_text", + "text": "Notification Channel" + }, + "element": { + "type": "channels_select", + "action_id": "channel_select", + "placeholder": { + "type": "plain_text", + "text": "Select channel for notifications" + } + }, + "optional": false + } + ] +} +``` + +View this modal in Block Kit Builder [here](https://app.slack.com/block-kit-builder/#%7B%22type%22:%22modal%22,%22callback_id%22:%22approve_delivery_view%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Approve%20Delivery%22%7D,%22private_metadata%22:%22%7Bdelivery_id%7D%22,%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Approving%20delivery%20*%7Bdelivery_id%7D*%22%7D%7D,%7B%22type%22:%22input%22,%22block_id%22:%22notes%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Additional%20delivery%20notes%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22notes_input%22,%22multiline%22:true,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Add%20notes...%22%7D%7D,%22optional%22:true%7D,%7B%22type%22:%22input%22,%22block_id%22:%22location%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Delivery%20Location%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22location_input%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Enter%20the%20location%20details...%22%7D%7D,%22optional%22:true%7D,%7B%22type%22:%22input%22,%22block_id%22:%22channel%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Notification%20Channel%22%7D,%22element%22:%7B%22type%22:%22channels_select%22,%22action_id%22:%22channel_select%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Select%20channel%20for%20notifications%22%7D%7D,%22optional%22:false%7D%5D,%22submit%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Approve%22%7D%7D). + +2. Within the `actions/sample_action.py` file, add the following function, replacing the view with the one you created above. Again, any strings with variables will be updated to f-strings and also any booleans will need to be capitalized. + +```python +def approve_delivery_callback(ack, body, client, logger: Logger): + try: + ack() + + delivery_id = body["message"]["text"].split("*")[1] + # Updates the original message so you can't press it twice + client.chat_update( + channel=body["container"]["channel_id"], + ts=body["container"]["message_ts"], + blocks=[ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"Processed delivery *{delivery_id}*...", + }, + } + ], + ) + + # Open a modal to gather information from the user + client.views_open( + trigger_id=body["trigger_id"], + view={} # Add your view here + ) + + logger.info(f"Approval modal opened by user {body['user']['id']}") + except Exception as e: + logger.error(e) +``` + +Similar to the `deny` button, we need to hook up all the connections. Your `actions/__init__.py` should look something like this: + +```python +from slack_bolt import App +from .sample_action import deny_delivery_callback +from .sample_action import approve_delivery_callback + + +def register(app: App): + app.action("approve_delivery")(approve_delivery_callback) + app.action("deny_delivery")(deny_delivery_callback) +``` + +Test your app by typing in a confirmation number in channel, click the confirm button and see if the modal comes up and you are able to capture information from the user. + +### Submitting the form + +Lastly, we’ll handle the submission of the form, which will trigger two things. We want to send the information into the specified channel, which will let the user know that the form was successful, as well as send the information into our system of record, Salesforce. + +1. Here’s a simple example of a message that you can use to present the information in channel. + +```json + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "✅ Delivery *{delivery_id}* approved:" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Delivery Notes:*\n{notes or 'None'}" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Delivery Location:*\n{loc or 'None'}" + } + } + ] +``` + +View this in Block Kit Builder [here](https://app.slack.com/block-kit-builder/?1#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22%E2%9C%85%20Delivery%20*%7Bdelivery_id%7D*%20approved:%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Delivery%20Notes:*%5Cn%7Bnotes%20or%20'None'%7D%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Delivery%20Location:*%5Cn%7Bloc%20or%20'None'%7D%22%7D%7D%5D%7D). Modify it however you like and then place it within the code below in the `/views/sample_views.py` file. + +```python +def handle_approve_delivery_view(ack, client, view, logger: Logger): + try: + ack() + + delivery_id = view["private_metadata"] + values = view["state"]["values"] + notes = values["notes"]["notes_input"]["value"] + loc = values["location"]["location_input"]["value"] + channel = values["channel"]["channel_select"]["selected_channel"] + + client.chat_postMessage( + channel=channel, + blocks=[], ## Add your message here + ) + + except Exception as e: + logger.error(f"Error in approve_delivery_view: {e}") +``` + +2. Making the connections in the `/views/__init__.py `file, we can test that this works by sending a message once again in our test channel. + +```python +from slack_bolt import App +from .sample_view import handle_approve_delivery_view + +def register(app: App): + app.view("sample_view_id")(sample_view_callback) # This can be deleted + app.view("approve_delivery_view")(handle_approve_delivery_view) ## Add this line +``` + +3. Let’s also send the information to Salesforce. There are [several ways](https://github.com/simple-salesforce/simple-salesforce?tab=readme-ov-file#examples) for you to access Salesforce through its API, but in this example, we’ve utilized `username`, `password` and `token` parameters. If you need help with getting your API token for Salesforce, take a look at [this article](https://help.salesforce.com/s/articleView?id=xcloud.user_security_token.htm&type=5). You’ll need to add these values as environment variables like we did earlier with our Slack tokens. You can use the following commands: + +```bash +export SF_USERNAME= +export SF_PASSWORD= +export SF_TOKEN= +``` + +4. We’re going to use assume that order information is stored in the Order object and that the confirmation IDs map to the 8-digit Order numbers within Salesforce. Given that assumption, we need to make a query to find the correct object, add the inputted information, and we’re done. Place this functionality before the last excerpt within the `/views/sample_views.py` file. + +```python +# Extract just the numeric portion from delivery_id + delivery_number = "".join(filter(str.isdigit, delivery_id)) + + # Update Salesforce order object + try: + sf = Salesforce( + username=os.environ.get("SF_USERNAME"), + password=os.environ.get("SF_PASSWORD"), + security_token=os.environ.get("SF_TOKEN"), + ) + + # Assuming delivery_id maps to Salesforce Order number + order = sf.query(f"SELECT Id FROM Order WHERE OrderNumber = '{delivery_number}'") # noqa: E501 + if order["records"]: + order_id = order["records"][0]["Id"] + sf.Order.update( + order_id, + { + "Status": "Delivered", + "Description": notes, + "Shipping_Location__c": loc, + }, + ) + logger.info(f"Updated order {delivery_id}") + else: + logger.warning(f"No order found for {delivery_id}") + + except Exception as sf_error: + logger.error(f"Update failed for order {delivery_id}: {sf_error}") + # Continue execution even if Salesforce update fails +``` + +You’ll also need to add the two imports that are found within this code to the top of the file. + +```python +import os +from simple_salesforce import Salesforce +``` + +With these imports, add `simple_salesforce` to your `requirements.txt` file, then install that package with the following command once again. + +```bash +pip install -r requirements.txt +``` + +![Image of delivery tracker app](/img/bolt-python/delivery-tracker-main.png) + +## Testing your app + +Test your app one last time, and you’re done! + +Congratulations! You’ve built an app using [Bolt for Python](/tools/bolt-python/) that allows you to send information into Slack, as well as into a third-party service. While there are more features you can add to make this a more robust app, we hope that this serves as a good introduction into connecting services like Salesforce using Slack as a conduit. \ No newline at end of file diff --git a/docs/img/delivery-tracker-main.png b/docs/img/delivery-tracker-main.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d2e885c9ed00d1537203192beb5f7228c6ebef GIT binary patch literal 65799 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%0A3K0U$bGe{Ox5WC%Nw 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%0D9_<|{L1-${Mz}&0e3f!dsLgTY1)_> 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 Awg3PC literal 0 HcmV?d00001 From 0c1442669114693dbceb12fdae725b4b31a5eb87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 10:23:44 -0500 Subject: [PATCH 159/282] chore(deps): bump actions/stale from 10.0.0 to 10.1.0 (#1393) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/triage-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index 5cb75bf93..85ccb72aa 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -16,7 +16,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 + - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 with: days-before-issue-stale: 30 days-before-issue-close: 10 From 1264ee1bff50bcf5b2e2d00dd69f595f936d0186 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 5 Nov 2025 11:42:22 -0500 Subject: [PATCH 160/282] chore: automate release process (#1394) --- .github/maintainers_guide.md | 200 +++++++++--------- .github/release.yml | 24 +++ .github/workflows/pypi-release.yml | 87 ++++++++ scripts/deploy_to_pypi_org.sh | 12 -- ...est_pypi_org.sh => deploy_to_test_pypi.sh} | 0 5 files changed, 212 insertions(+), 111 deletions(-) create mode 100644 .github/release.yml create mode 100644 .github/workflows/pypi-release.yml delete mode 100755 scripts/deploy_to_pypi_org.sh rename scripts/{deploy_to_test_pypi_org.sh => deploy_to_test_pypi.sh} (100%) diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index 352398072..8cd600271 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -10,14 +10,14 @@ this project. If you use this package within your own software as is but don't p We recommend using [pyenv](https://github.com/pyenv/pyenv) for Python runtime management. If you use macOS, follow the following steps: -```bash -$ brew update -$ brew install pyenv +```sh +brew update +brew install pyenv ``` Install necessary Python runtimes for development/testing. You can rely on GitHub Actions workflows for testing with various major versions. -```bash +```sh $ pyenv install -l | grep -v "-e[conda|stackless|pypy]" $ pyenv install 3.8.5 # select the latest patch version @@ -34,9 +34,9 @@ $ pyenv rehash Then, you can create a new Virtual Environment this way: -```bash -$ python -m venv env_3.8.5 -$ source env_3.8.5/bin/activate +```sh +python -m venv env_3.8.5 +source env_3.8.5/bin/activate ``` ## Tasks @@ -49,27 +49,27 @@ If you make some changes to this SDK, please write corresponding unit tests as m If this is your first time to run tests, although it may take a bit long time, running the following script is the easiest. -```bash -$ ./scripts/install_all_and_run_tests.sh +```sh +./scripts/install_all_and_run_tests.sh ``` Once you installed all the required dependencies, you can use the following one. -```bash -$ ./scripts/run_tests.sh +```sh +./scripts/run_tests.sh ``` Also, you can run a single test this way. -```bash -$ ./scripts/run_tests.sh tests/scenario_tests/test_app.py +```sh +./scripts/run_tests.sh tests/scenario_tests/test_app.py ``` #### Run the Samples If you make changes to `slack_bolt/adapter/*`, please verify if it surely works by running the apps under `examples` directory. -```bash +```sh # Install all optional dependencies $ pip install -r requirements/adapter.txt $ pip install -r requirements/adapter_testing.txt @@ -97,121 +97,123 @@ If you want to test the package locally you can. 1. Build the package locally - Run - ```bash + ```sh scripts/build_pypi_package.sh ``` - This will create a `.whl` file in the `./dist` folder 2. Use the built package - Example `/dist/slack_bolt-1.2.3-py2.py3-none-any.whl` was created - From anywhere on your machine you can install this package to a project with - ```bash + ```sh pip install /dist/slack_bolt-1.2.3-py2.py3-none-any.whl ``` - It is also possible to include `slack_bolt @ file:////dist/slack_bolt-1.2.3-py2.py3-none-any.whl` in a [requirements.txt](https://pip.pypa.io/en/stable/user_guide/#requirements-files) file -### Releasing - -#### Generate API reference documents +### Generate API reference documents -```bash +```sh ./scripts/generate_api_docs.sh ``` +### Releasing + #### test.pypi.org deployment -##### $HOME/.pypirc +[TestPyPI](https://test.pypi.org/) is a separate instance of the Python Package +Index that allows you to try distribution tools and processes without affecting +the real index. This is particularly useful when making changes related to the +package configuration itself, for example, modifications to the `pyproject.toml` file. + +You can deploy this project to TestPyPI using GitHub Actions. -```toml -[testpypi] -username: {your username} -password: {your password} +To deploy using GitHub Actions: + +1. Push your changes to a branch or tag +2. Navigate to +3. Click on "Run workflow" +4. Select your branch or tag from the dropdown +5. Click "Run workflow" to build and deploy your branch to TestPyPI + +Alternatively, you can deploy from your local machine with: + +```sh +./scripts/deploy_to_test_pypi.sh ``` #### Development Deployment -1. Create a branch in which the development release will live: - - Bump the version number in adherence to [Semantic Versioning](http://semver.org/) and [Developmental Release](https://peps.python.org/pep-0440/#developmental-releases) in `slack_bolt/version.py` - - Example the current version is `1.2.3` a proper development bump would be `1.3.0.dev0` +Deploying a new version of this library to PyPI is triggered by publishing a GitHub Release. +Before creating a new release, ensure that everything on a stable branch has +landed, then [run the tests](#run-all-the-unit-tests). + +1. Create the commit for the release + 1. In `slack_bolt/version.py` bump the version number in adherence to [Semantic Versioning](http://semver.org/) and [Developmental Release](https://peps.python.org/pep-0440/#developmental-releases). + - Example: if the current version is `1.2.3`, a proper development bump would be `1.2.4.dev0` - `.dev` will indicate to pip that this is a [Development Release](https://peps.python.org/pep-0440/#developmental-releases) - - Note that the `dev` version can be bumped in development releases: `1.3.0.dev0` -> `1.3.0.dev1` - - Commit with a message including the new version number. For example `1.3.0.dev0` & Push the commit to a branch where the development release will live (create it if it does not exist) - - `git checkout -b future-release` - - `git commit -m 'version 1.3.0.dev0'` - - `git push future-release` - - Create a git tag for the release. For example `git tag v1.3.0.dev0`. - - Push the tag up to github with `git push origin --tags` - -2. Distribute the release - - Use the latest stable Python runtime - - `python -m venv .venv` - - `./scripts/deploy_to_pypi_org.sh` - - You do not need to create a GitHub release - -3. (Slack Internal) Communicate the release internally + - Note that the `dev` version can be bumped in development releases: `1.2.4.dev0` -> `1.2.4.dev1` + 2. Build the docs with `./scripts/generate_api_docs.sh`. + 3. Commit with a message including the new version number. For example `1.2.4.dev0` & push the commit to a branch where the development release will live (create it if it does not exist) + 1. `git checkout -b future-release` + 2. `git commit -m 'chore(release): version 1.2.4.dev0'` + 3. `git push -u origin future-release` +2. Create a new GitHub Release + 1. Navigate to the [Releases page](https://github.com/slackapi/bolt-python/releases). + 2. Click the "Draft a new release" button. + 3. Set the "Target" to the feature branch with the development changes. + 4. Click "Tag: Select tag" + 5. Input a new tag name manually. The tag name must match the version in `slack_bolt/version.py` prefixed with "v" (e.g., if version is `1.2.4.dev0`, enter `v1.2.4.dev0`) + 6. Click the "Create a new tag" button. This won't create your tag immediately. + 7. Click the "Generate release notes" button. + 8. The release name should match the tag name! + 9. Edit the resulting notes to ensure they have decent messaging that is understandable by non-contributors, but each commit should still have its own line. + 10. Set this release as a pre-release. + 11. Publish the release by clicking the "Publish release" button! +3. Navigate to the [release workflow run](https://github.com/slackapi/bolt-python/actions/workflows/pypi-release.yml). You will need to approve the deployment! +4. After a few minutes, the corresponding version will be available on . +5. (Slack Internal) Communicate the release internally #### Production Deployment -1. Create the commit for the release: - - Bump the version number in adherence to [Semantic Versioning](http://semver.org/) in `slack_bolt/version.py` - - Build the docs with `./scripts/generate_api_docs.sh`. - - Commit with a message including the new version number. For example `1.2.3` & Push the commit to a branch and create a PR to sanity check. - - `git checkout -b v1.2.3` - - `git commit -a -m 'version 1.2.3'` - - `git push -u origin HEAD` - - Open a PR and merge after receiving at least one approval from other maintainers. - -2. Distribute the release - - Use the latest stable Python runtime - - `git checkout main && git pull` - - `python --version` - - `python -m venv .venv` - - `./scripts/deploy_to_pypi_org.sh` - - Create a new GitHub Release from the [Releases page](https://github.com/slackapi/bolt-python/releases) by clicking the "Draft a new release" button. - - Enter the new version number updated from the commit (e.g. `v1.2.3`) into the "Choose a tag" input. - - Ensure the tag `Target` branch is `main` (e.g `Target:main`). - - Click the "Create a new tag: x.x.x on publish" button. This won't create your tag immediately. - - Name the release after the version number updated from the commit (e.g. `version 1.2.3`) - - Auto-generate the release notes by clicking the "Auto-generate release - notes" button. This will pull in changes that will be included in your - release. - - Edit the resulting notes to ensure they have decent messaging that are - understandable by non-contributors, but each commit should still have it's - own line. - - Ensure that this version adheres to [semantic versioning](http://semver.org/). See - [Versioning](#versioning-and-tags) for correct version format. Version tags - should match the following pattern: `v2.5.0`. - - ```markdown - ## New Features - - ### Awesome Feature 1 - - Description here. - - ### Awesome Feature 2 - - Description here. - - ## Changes - - * #123 Make it better - thanks @SlackHQ - * #123 Fix something wrong - thanks @seratch - ``` - -3. (Slack Internal) Communicate the release internally - - Include a link to the GitHub release - -4. Make announcements - - #tools-bolt in community.slack.com - -5. (Slack Internal) Tweet by @SlackAPI - - Not necessary for patch updates, might be needed for minor updates, definitely needed for major updates. Include a link to the GitHub release +Deploying a new version of this library to PyPI is triggered by publishing a GitHub Release. +Before creating a new release, ensure that everything on the `main` branch since +the last tag is in a releasable state! At a minimum, [run the tests](#run-all-the-unit-tests). + +1. Create the commit for the release + 1. In `slack_bolt/version.py` bump the version number in adherence to [Semantic Versioning](http://semver.org/) and the [Versioning](#versioning-and-tags) section. + 2. Build the docs with `./scripts/generate_api_docs.sh`. + 3. Commit with a message including the new version number. For example `1.2.3` & push the commit to a branch and create a PR to sanity check. + 1. `git checkout -b 1.2.3-release` + 2. `git commit -m 'chore(release): version 1.2.3'` + 3. `git push -u origin 1.2.3-release` + 4. Add relevant labels to the PR and add the PR to a GitHub Milestone. + 5. Merge in release PR after getting an approval from at least one maintainer. +2. Create a new GitHub Release + 1. Navigate to the [Releases page](https://github.com/slackapi/bolt-python/releases). + 2. Click the "Draft a new release" button. + 3. Set the "Target" to the `main` branch. + 4. Click "Tag: Select tag" + 5. Input a new tag name manually. The tag name must match the version in `slack_bolt/version.py` prefixed with "v" (e.g., if version is `1.2.3`, enter `v1.2.3`) + 6. Click the "Create a new tag" button. This won't create your tag immediately. + 7. Click the "Generate release notes" button. + 8. The release name should match the tag name! + 9. Edit the resulting notes to ensure they have decent messaging that is understandable by non-contributors, but each commit should still have its own line. + 10. Include a link to the current GitHub Milestone. + 11. Ensure the "latest release" checkbox is checked to mark this as the latest stable release. + 12. Publish the release by clicking the "Publish release" button! +3. Navigate to the [release workflow run](https://github.com/slackapi/bolt-python/actions/workflows/pypi-release.yml). You will need to approve the deployment! +4. After a few minutes, the corresponding version will be available on . +5. Close the current GitHub Milestone and create one for the next patch version. +6. (Slack Internal) Communicate the release internally + - Include a link to the GitHub release +7. (Slack Internal) Tweet by @SlackAPI + - Not necessary for patch updates, might be needed for minor updates, + definitely needed for major updates. Include a link to the GitHub release ## Workflow ### Versioning and Tags -This project uses semantic versioning, expressed through the numbering scheme of +This project uses [Semantic Versioning](http://semver.org/), expressed through the numbering scheme of [PEP-0440](https://www.python.org/dev/peps/pep-0440/). ### Branches diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000..b2574b7cc --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,24 @@ +# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes +changelog: + categories: + - title: 🚀 Enhancements + labels: + - enhancement + - title: 🐛 Bug Fixes + labels: + - bug + - title: 📚 Documentation + labels: + - docs + - title: 🤖 Build + labels: + - build + - title: 🧪 Testing/Code Health + labels: + - code health + - title: 🔒 Security + labels: + - security + - title: 📦 Other changes + labels: + - "*" diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml new file mode 100644 index 000000000..21b472247 --- /dev/null +++ b/.github/workflows/pypi-release.yml @@ -0,0 +1,87 @@ +name: Upload A Release to pypi.org or test.pypi.org + +on: + release: + types: + - published + workflow_dispatch: + inputs: + dry_run: + description: "Dry run (build only, do not publish)" + required: false + type: boolean + +jobs: + release-build: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ github.event.release.tag_name || github.ref }} + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: "3.x" + + - name: Build release distributions + run: | + scripts/build_pypi_package.sh + + - name: Persist dist folder + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: release-dist + path: dist/ + + test-pypi-publish: + runs-on: ubuntu-latest + needs: + - release-build + # Run this job for workflow_dispatch events when dry_run input is not 'true' + # Note: The comparison is against a string value 'true' since GitHub Actions inputs are strings + if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run != 'true' + environment: + name: testpypi + permissions: + id-token: write + + steps: + - name: Retrieve dist folder + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: release-dist + path: dist/ + + - name: Publish release distributions to test.pypi.org + # Using OIDC for PyPI publishing (no API tokens needed) + # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + with: + repository-url: https://test.pypi.org/legacy/ + + pypi-publish: + runs-on: ubuntu-latest + needs: + - release-build + if: github.event_name == 'release' + environment: + name: pypi + permissions: + id-token: write + + steps: + - name: Retrieve dist folder + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: release-dist + path: dist/ + + - name: Publish release distributions to pypi.org + # Using OIDC for PyPI publishing (no API tokens needed) + # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 diff --git a/scripts/deploy_to_pypi_org.sh b/scripts/deploy_to_pypi_org.sh deleted file mode 100755 index 8c5234902..000000000 --- a/scripts/deploy_to_pypi_org.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -script_dir=`dirname $0` -cd ${script_dir}/.. -rm -rf ./slack_bolt.egg-info - -pip install -U pip && \ - pip install -U twine build && \ - rm -rf dist/ build/ slack_bolt.egg-info/ && \ - python -m build --sdist --wheel && \ - twine check dist/* && \ - twine upload dist/* diff --git a/scripts/deploy_to_test_pypi_org.sh b/scripts/deploy_to_test_pypi.sh similarity index 100% rename from scripts/deploy_to_test_pypi_org.sh rename to scripts/deploy_to_test_pypi.sh From 2086c7a4f5e4c010ef04f5537668d7b228f1bf59 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Nov 2025 08:54:26 -0800 Subject: [PATCH 161/282] ci: upload test results using the recommended codecov action (#1396) --- .github/workflows/codecov.yml | 3 ++- .github/workflows/tests.yml | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 7381117bb..b402079c1 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -39,5 +39,6 @@ jobs: uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: fail_ci_if_error: true - verbose: true + report_type: coverage token: ${{ secrets.CODECOV_TOKEN }} + verbose: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 167dc2ce4..20262a857 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -76,10 +76,12 @@ jobs: pytest tests/scenario_tests_async/ --junitxml=reports/test_scenario_async.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1 + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: directory: ./reports/ + fail_ci_if_error: true flags: ${{ matrix.python-version }} + report_type: test_results token: ${{ secrets.CODECOV_TOKEN }} verbose: true notifications: From fe28b14d6fc78cae56cb6fad28d3357cd639c6a8 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 13 Nov 2025 14:42:49 -0500 Subject: [PATCH 162/282] feat: add support for python 3.14 (#1397) --- .github/workflows/codecov.yml | 2 +- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .github/workflows/tests.yml | 1 + pyproject.toml | 3 ++- slack_bolt/listener/async_listener.py | 6 ------ slack_bolt/listener_matcher/async_listener_matcher.py | 2 -- 7 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index b402079c1..02c318a20 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - python-version: ["3.13"] + python-version: ["3.14"] permissions: contents: read env: diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index bd4e3dfd8..f777996b4 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - python-version: ["3.13"] + python-version: ["3.14"] permissions: contents: read steps: diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 1bf4abf0d..52d59c830 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - python-version: ["3.13"] + python-version: ["3.14"] permissions: contents: read steps: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20262a857..42fd58ef6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,6 +24,7 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" permissions: contents: read steps: diff --git a/pyproject.toml b/pyproject.toml index 5361ef1b4..a5c12548b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,12 +15,13 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] requires-python = ">=3.7" -dependencies = ["slack_sdk>=3.37.0,<4"] +dependencies = ["slack_sdk>=3.38.0,<4"] [project.urls] diff --git a/slack_bolt/listener/async_listener.py b/slack_bolt/listener/async_listener.py index 0810b91a7..1717b1a8d 100644 --- a/slack_bolt/listener/async_listener.py +++ b/slack_bolt/listener/async_listener.py @@ -72,13 +72,7 @@ async def run_ack_function(self, *, request: AsyncBoltRequest, response: BoltRes from logging import Logger -from typing import Callable, Awaitable - -from slack_bolt.listener_matcher.async_listener_matcher import AsyncListenerMatcher from slack_bolt.logger import get_bolt_app_logger -from slack_bolt.middleware.async_middleware import AsyncMiddleware -from slack_bolt.request.async_request import AsyncBoltRequest -from slack_bolt.response import BoltResponse class AsyncCustomListener(AsyncListener): diff --git a/slack_bolt/listener_matcher/async_listener_matcher.py b/slack_bolt/listener_matcher/async_listener_matcher.py index 83e04e478..3230bb342 100644 --- a/slack_bolt/listener_matcher/async_listener_matcher.py +++ b/slack_bolt/listener_matcher/async_listener_matcher.py @@ -25,8 +25,6 @@ async def async_matches(self, req: AsyncBoltRequest, resp: BoltResponse) -> bool from slack_bolt.kwargs_injection.async_utils import build_async_required_kwargs from slack_bolt.logger import get_bolt_app_logger -from slack_bolt.request.async_request import AsyncBoltRequest -from slack_bolt.response import BoltResponse class AsyncCustomListenerMatcher(AsyncListenerMatcher): From 87e342515d104e151cb849f50a0068712ae9a76c Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Thu, 13 Nov 2025 11:46:33 -0800 Subject: [PATCH 163/282] chore: Add .github/CODEOWNERS file (#1398) --- .github/CODEOWNERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..4a08579c2 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,12 @@ +# Salesforce Open Source project configuration +# Learn more: https://github.com/salesforce/oss-template +#ECCN:Open Source +#GUSINFO:Open Source,Open Source Workflow + +# @slackapi/slack-platform-python +# are code reviewers for all changes in this repo. +* @slackapi/slack-platform-python + +# @slackapi/developer-education +# are code reviewers for changes in the `/docs` directory. +/docs/ @slackapi/developer-education From 9cba291465237eeac6416730fc6b3838e1078b30 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 13 Nov 2025 15:13:10 -0500 Subject: [PATCH 164/282] chore(release): version 1.27.0 (#1399) --- .../authorization/authorize_result.html | 4 +- docs/reference/authorization/index.html | 4 +- .../thread_context_store/file/index.html | 2 +- .../context/complete/async_complete.html | 40 ++++++++- docs/reference/context/complete/complete.html | 40 ++++++++- docs/reference/context/complete/index.html | 40 ++++++++- docs/reference/context/fail/async_fail.html | 40 ++++++++- docs/reference/context/fail/fail.html | 40 ++++++++- docs/reference/context/fail/index.html | 40 ++++++++- docs/reference/error/index.html | 2 +- docs/reference/index.html | 82 ++++++++++++++++++- docs/reference/logger/messages.html | 4 +- .../reference/oauth/async_oauth_settings.html | 2 +- docs/reference/oauth/oauth_settings.html | 2 +- slack_bolt/version.py | 2 +- 15 files changed, 324 insertions(+), 20 deletions(-) diff --git a/docs/reference/authorization/authorize_result.html b/docs/reference/authorization/authorize_result.html index 6eac3724d..d53c5cd5c 100644 --- a/docs/reference/authorization/authorize_result.html +++ b/docs/reference/authorization/authorize_result.html @@ -48,7 +48,7 @@

    Classes

    class AuthorizeResult -(*,
    enterprise_id: str | None,
    team_id: str | None,
    team: str | None = None,
    url: str | None = None,
    bot_user_id: str | None = None,
    bot_id: str | None = None,
    bot_token: str | None = None,
    bot_scopes: str | Sequence[str] | None = None,
    user_id: str | None = None,
    user: str | None = None,
    user_token: str | None = None,
    user_scopes: str | Sequence[str] | None = None)
    +(*,
    enterprise_id: str | None,
    team_id: str | None,
    team: str | None = None,
    url: str | None = None,
    bot_user_id: str | None = None,
    bot_id: str | None = None,
    bot_token: str | None = None,
    bot_scopes: Sequence[str] | str | None = None,
    user_id: str | None = None,
    user: str | None = None,
    user_token: str | None = None,
    user_scopes: Sequence[str] | str | None = None)
    @@ -246,7 +246,7 @@

    Class variables

    Static methods

    -def from_auth_test_response(*,
    bot_token: str | None = None,
    user_token: str | None = None,
    bot_scopes: str | Sequence[str] | None = None,
    user_scopes: str | Sequence[str] | None = None,
    auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
    user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
    +def from_auth_test_response(*,
    bot_token: str | None = None,
    user_token: str | None = None,
    bot_scopes: Sequence[str] | str | None = None,
    user_scopes: Sequence[str] | str | None = None,
    auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse,
    user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse | None = None)
    diff --git a/docs/reference/authorization/index.html b/docs/reference/authorization/index.html index 19de311df..2fdd1f916 100644 --- a/docs/reference/authorization/index.html +++ b/docs/reference/authorization/index.html @@ -75,7 +75,7 @@

    Classes

    class AuthorizeResult -(*,
    enterprise_id: str | None,
    team_id: str | None,
    team: str | None = None,
    url: str | None = None,
    bot_user_id: str | None = None,
    bot_id: str | None = None,
    bot_token: str | None = None,
    bot_scopes: str | Sequence[str] | None = None,
    user_id: str | None = None,
    user: str | None = None,
    user_token: str | None = None,
    user_scopes: str | Sequence[str] | None = None)
    +(*,
    enterprise_id: str | None,
    team_id: str | None,
    team: str | None = None,
    url: str | None = None,
    bot_user_id: str | None = None,
    bot_id: str | None = None,
    bot_token: str | None = None,
    bot_scopes: Sequence[str] | str | None = None,
    user_id: str | None = None,
    user: str | None = None,
    user_token: str | None = None,
    user_scopes: Sequence[str] | str | None = None)
    @@ -273,7 +273,7 @@

    Class variables

    Static methods

    -def from_auth_test_response(*,
    bot_token: str | None = None,
    user_token: str | None = None,
    bot_scopes: str | Sequence[str] | None = None,
    user_scopes: str | Sequence[str] | None = None,
    auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
    user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
    +def from_auth_test_response(*,
    bot_token: str | None = None,
    user_token: str | None = None,
    bot_scopes: Sequence[str] | str | None = None,
    user_scopes: Sequence[str] | str | None = None,
    auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse,
    user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse | None = None)
    diff --git a/docs/reference/context/assistant/thread_context_store/file/index.html b/docs/reference/context/assistant/thread_context_store/file/index.html index cbb4e4db6..4a5d944e1 100644 --- a/docs/reference/context/assistant/thread_context_store/file/index.html +++ b/docs/reference/context/assistant/thread_context_store/file/index.html @@ -48,7 +48,7 @@

    Classes

    class FileAssistantThreadContextStore -(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts')
    diff --git a/docs/reference/context/complete/async_complete.html b/docs/reference/context/complete/async_complete.html index 36cf1f92f..f0546a950 100644 --- a/docs/reference/context/complete/async_complete.html +++ b/docs/reference/context/complete/async_complete.html @@ -58,6 +58,7 @@

    Classes

    class AsyncComplete:
         client: AsyncWebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -66,6 +67,7 @@ 

    Classes

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False async def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> AsyncSlackResponse: """Signal the successful completion of the custom function. @@ -82,9 +84,18 @@

    Classes

    if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") + self._called = True return await self.client.functions_completeSuccess( function_execution_id=self.function_execution_id, outputs=outputs or {} - )
    + ) + + def has_been_called(self) -> bool: + """Check if this complete function has been called. + + Returns: + bool: True if the complete function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -98,6 +109,32 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this complete function has been called.
    +
    +    Returns:
    +        bool: True if the complete function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this complete function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the complete function has been called, False otherwise.
    +
    +
    +
    @@ -119,6 +156,7 @@

  • client
  • function_execution_id
  • +
  • has_been_called
  • diff --git a/docs/reference/context/complete/complete.html b/docs/reference/context/complete/complete.html index b1f01ea1a..b8c1b083b 100644 --- a/docs/reference/context/complete/complete.html +++ b/docs/reference/context/complete/complete.html @@ -58,6 +58,7 @@

    Classes

    class Complete:
         client: WebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -66,6 +67,7 @@ 

    Classes

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: """Signal the successful completion of the custom function. @@ -82,7 +84,16 @@

    Classes

    if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") - return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {})
    + self._called = True + return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {}) + + def has_been_called(self) -> bool: + """Check if this complete function has been called. + + Returns: + bool: True if the complete function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -96,6 +107,32 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this complete function has been called.
    +
    +    Returns:
    +        bool: True if the complete function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this complete function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the complete function has been called, False otherwise.
    +
    +
    +
    @@ -117,6 +154,7 @@

  • client
  • function_execution_id
  • +
  • has_been_called
  • diff --git a/docs/reference/context/complete/index.html b/docs/reference/context/complete/index.html index 7665622b6..dddd26a84 100644 --- a/docs/reference/context/complete/index.html +++ b/docs/reference/context/complete/index.html @@ -69,6 +69,7 @@

    Classes

    class Complete:
         client: WebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -77,6 +78,7 @@ 

    Classes

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: """Signal the successful completion of the custom function. @@ -93,7 +95,16 @@

    Classes

    if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") - return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {})
    + self._called = True + return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {}) + + def has_been_called(self) -> bool: + """Check if this complete function has been called. + + Returns: + bool: True if the complete function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -107,6 +118,32 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this complete function has been called.
    +
    +    Returns:
    +        bool: True if the complete function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this complete function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the complete function has been called, False otherwise.
    +
    +
    +
    @@ -134,6 +171,7 @@

  • client
  • function_execution_id
  • +
  • has_been_called
  • diff --git a/docs/reference/context/fail/async_fail.html b/docs/reference/context/fail/async_fail.html index 6b3e4f1df..80f19d18c 100644 --- a/docs/reference/context/fail/async_fail.html +++ b/docs/reference/context/fail/async_fail.html @@ -58,6 +58,7 @@

    Classes

    class AsyncFail:
         client: AsyncWebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -66,6 +67,7 @@ 

    Classes

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False async def __call__(self, error: str) -> AsyncSlackResponse: """Signal that the custom function failed to complete. @@ -82,7 +84,16 @@

    Classes

    if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") - return await self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error)
    + self._called = True + return await self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) + + def has_been_called(self) -> bool: + """Check if this fail function has been called. + + Returns: + bool: True if the fail function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -96,6 +107,32 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this fail function has been called.
    +
    +    Returns:
    +        bool: True if the fail function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this fail function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the fail function has been called, False otherwise.
    +
    +
    +
    @@ -117,6 +154,7 @@

  • client
  • function_execution_id
  • +
  • has_been_called
  • diff --git a/docs/reference/context/fail/fail.html b/docs/reference/context/fail/fail.html index 0152561d8..51f4896a4 100644 --- a/docs/reference/context/fail/fail.html +++ b/docs/reference/context/fail/fail.html @@ -58,6 +58,7 @@

    Classes

    class Fail:
         client: WebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -66,6 +67,7 @@ 

    Classes

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, error: str) -> SlackResponse: """Signal that the custom function failed to complete. @@ -82,7 +84,16 @@

    Classes

    if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") - return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error)
    + self._called = True + return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) + + def has_been_called(self) -> bool: + """Check if this fail function has been called. + + Returns: + bool: True if the fail function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -96,6 +107,32 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this fail function has been called.
    +
    +    Returns:
    +        bool: True if the fail function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this fail function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the fail function has been called, False otherwise.
    +
    +
    +
    @@ -117,6 +154,7 @@

  • client
  • function_execution_id
  • +
  • has_been_called
  • diff --git a/docs/reference/context/fail/index.html b/docs/reference/context/fail/index.html index eb2653106..3b35dd6aa 100644 --- a/docs/reference/context/fail/index.html +++ b/docs/reference/context/fail/index.html @@ -69,6 +69,7 @@

    Classes

    class Fail:
         client: WebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -77,6 +78,7 @@ 

    Classes

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, error: str) -> SlackResponse: """Signal that the custom function failed to complete. @@ -93,7 +95,16 @@

    Classes

    if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") - return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error)
    + self._called = True + return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) + + def has_been_called(self) -> bool: + """Check if this fail function has been called. + + Returns: + bool: True if the fail function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -107,6 +118,32 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this fail function has been called.
    +
    +    Returns:
    +        bool: True if the fail function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this fail function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the fail function has been called, False otherwise.
    +
    +
    +
    @@ -134,6 +171,7 @@

  • client
  • function_execution_id
  • +
  • has_been_called
  • diff --git a/docs/reference/error/index.html b/docs/reference/error/index.html index f57d690e9..9a9998e63 100644 --- a/docs/reference/error/index.html +++ b/docs/reference/error/index.html @@ -72,7 +72,7 @@

    Subclasses

    class BoltUnhandledRequestError -(*,
    request: ForwardRef('BoltRequest') | ForwardRef('AsyncBoltRequest'),
    current_response: ForwardRef('BoltResponse') | None,
    last_global_middleware_name: str | None = None)
    +(*,
    request: BoltRequest | AsyncBoltRequest,
    current_response: BoltResponse | None,
    last_global_middleware_name: str | None = None)
    diff --git a/docs/reference/index.html b/docs/reference/index.html index 7bd6d117e..1c02a8aeb 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -5073,6 +5073,7 @@

    Methods

    class Complete:
         client: WebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -5081,6 +5082,7 @@ 

    Methods

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: """Signal the successful completion of the custom function. @@ -5097,7 +5099,16 @@

    Methods

    if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") - return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {})
    + self._called = True + return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {}) + + def has_been_called(self) -> bool: + """Check if this complete function has been called. + + Returns: + bool: True if the complete function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -5111,6 +5122,32 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this complete function has been called.
    +
    +    Returns:
    +        bool: True if the complete function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this complete function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the complete function has been called, False otherwise.
    +
    +
    +
    class CustomListenerMatcher @@ -5189,6 +5226,7 @@

    Inherited members

    class Fail:
         client: WebClient
         function_execution_id: Optional[str]
    +    _called: bool
     
         def __init__(
             self,
    @@ -5197,6 +5235,7 @@ 

    Inherited members

    ): self.client = client self.function_execution_id = function_execution_id + self._called = False def __call__(self, error: str) -> SlackResponse: """Signal that the custom function failed to complete. @@ -5213,7 +5252,16 @@

    Inherited members

    if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") - return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error)
    + self._called = True + return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) + + def has_been_called(self) -> bool: + """Check if this fail function has been called. + + Returns: + bool: True if the fail function has been called, False otherwise. + """ + return self._called

    Class variables

    @@ -5227,10 +5275,36 @@

    Class variables

    The type of the None singleton.

    +

    Methods

    +
    +
    +def has_been_called(self) ‑> bool +
    +
    +
    + +Expand source code + +
    def has_been_called(self) -> bool:
    +    """Check if this fail function has been called.
    +
    +    Returns:
    +        bool: True if the fail function has been called, False otherwise.
    +    """
    +    return self._called
    +
    +

    Check if this fail function has been called.

    +

    Returns

    +
    +
    bool
    +
    True if the fail function has been called, False otherwise.
    +
    +
    +
    class FileAssistantThreadContextStore -(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts')
    @@ -6120,6 +6194,7 @@

    Complete
  • client
  • function_execution_id
  • +
  • has_been_called
  • @@ -6136,6 +6211,7 @@

    Fail

  • client
  • function_execution_id
  • +
  • has_been_called
  • diff --git a/docs/reference/logger/messages.html b/docs/reference/logger/messages.html index 1072e6479..e69b45fc9 100644 --- a/docs/reference/logger/messages.html +++ b/docs/reference/logger/messages.html @@ -409,7 +409,7 @@

    Functions

  • -def warning_unhandled_by_global_middleware(name: str,
    req: BoltRequest | ForwardRef('AsyncBoltRequest')) ‑> str
    +def warning_unhandled_by_global_middleware(name: str,
    req: BoltRequest | AsyncBoltRequest) ‑> str
    @@ -427,7 +427,7 @@

    Functions

    -def warning_unhandled_request(req: BoltRequest | ForwardRef('AsyncBoltRequest')) ‑> str +def warning_unhandled_request(req: BoltRequest | AsyncBoltRequest) ‑> str
    diff --git a/docs/reference/oauth/async_oauth_settings.html b/docs/reference/oauth/async_oauth_settings.html index 5e6a543c4..3b8c04edb 100644 --- a/docs/reference/oauth/async_oauth_settings.html +++ b/docs/reference/oauth/async_oauth_settings.html @@ -48,7 +48,7 @@

    Classes

    class AsyncOAuthSettings -(*,
    client_id: str | None = None,
    client_secret: str | None = None,
    scopes: str | Sequence[str] | None = None,
    user_scopes: str | Sequence[str] | None = None,
    redirect_uri: str | None = None,
    install_path: str = '/slack/install',
    install_page_rendering_enabled: bool = True,
    redirect_uri_path: str = '/slack/oauth_redirect',
    callback_options: AsyncCallbackOptions | None = None,
    success_url: str | None = None,
    failure_url: str | None = None,
    authorization_url: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
    installation_store_bot_only: bool = False,
    token_rotation_expiration_minutes: int = 120,
    user_token_resolution: str = 'authed_user',
    state_validation_enabled: bool = True,
    state_store: slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore | None = None,
    state_cookie_name: str = 'slack-app-oauth-state',
    state_expiration_seconds: int = 600,
    logger: logging.Logger = <Logger slack_bolt.oauth.async_oauth_settings (WARNING)>)
    +(*,
    client_id: str | None = None,
    client_secret: str | None = None,
    scopes: Sequence[str] | str | None = None,
    user_scopes: Sequence[str] | str | None = None,
    redirect_uri: str | None = None,
    install_path: str = '/slack/install',
    install_page_rendering_enabled: bool = True,
    redirect_uri_path: str = '/slack/oauth_redirect',
    callback_options: AsyncCallbackOptions | None = None,
    success_url: str | None = None,
    failure_url: str | None = None,
    authorization_url: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
    installation_store_bot_only: bool = False,
    token_rotation_expiration_minutes: int = 120,
    user_token_resolution: str = 'authed_user',
    state_validation_enabled: bool = True,
    state_store: slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore | None = None,
    state_cookie_name: str = 'slack-app-oauth-state',
    state_expiration_seconds: int = 600,
    logger: logging.Logger = <Logger slack_bolt.oauth.async_oauth_settings (WARNING)>)
    diff --git a/docs/reference/oauth/oauth_settings.html b/docs/reference/oauth/oauth_settings.html index 1eb2ab7dd..cd8def497 100644 --- a/docs/reference/oauth/oauth_settings.html +++ b/docs/reference/oauth/oauth_settings.html @@ -48,7 +48,7 @@

    Classes

    class OAuthSettings -(*,
    client_id: str | None = None,
    client_secret: str | None = None,
    scopes: str | Sequence[str] | None = None,
    user_scopes: str | Sequence[str] | None = None,
    redirect_uri: str | None = None,
    install_path: str = '/slack/install',
    install_page_rendering_enabled: bool = True,
    redirect_uri_path: str = '/slack/oauth_redirect',
    callback_options: CallbackOptions | None = None,
    success_url: str | None = None,
    failure_url: str | None = None,
    authorization_url: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool = False,
    token_rotation_expiration_minutes: int = 120,
    user_token_resolution: str = 'authed_user',
    state_validation_enabled: bool = True,
    state_store: slack_sdk.oauth.state_store.state_store.OAuthStateStore | None = None,
    state_cookie_name: str = 'slack-app-oauth-state',
    state_expiration_seconds: int = 600,
    logger: logging.Logger = <Logger slack_bolt.oauth.oauth_settings (WARNING)>)
    +(*,
    client_id: str | None = None,
    client_secret: str | None = None,
    scopes: Sequence[str] | str | None = None,
    user_scopes: Sequence[str] | str | None = None,
    redirect_uri: str | None = None,
    install_path: str = '/slack/install',
    install_page_rendering_enabled: bool = True,
    redirect_uri_path: str = '/slack/oauth_redirect',
    callback_options: CallbackOptions | None = None,
    success_url: str | None = None,
    failure_url: str | None = None,
    authorization_url: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool = False,
    token_rotation_expiration_minutes: int = 120,
    user_token_resolution: str = 'authed_user',
    state_validation_enabled: bool = True,
    state_store: slack_sdk.oauth.state_store.state_store.OAuthStateStore | None = None,
    state_cookie_name: str = 'slack-app-oauth-state',
    state_expiration_seconds: int = 600,
    logger: logging.Logger = <Logger slack_bolt.oauth.oauth_settings (WARNING)>)
    diff --git a/slack_bolt/version.py b/slack_bolt/version.py index 8cfd6f900..9b1349aea 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.26.0" +__version__ = "1.27.0" From 8a8f285b6d89ab951628ac47591a1e22f99a6288 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 14 Nov 2025 09:48:03 -0500 Subject: [PATCH 165/282] fix: update the release instructions (#1400) --- .github/maintainers_guide.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index 8cd600271..f8edeeabd 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -147,15 +147,17 @@ Before creating a new release, ensure that everything on a stable branch has landed, then [run the tests](#run-all-the-unit-tests). 1. Create the commit for the release - 1. In `slack_bolt/version.py` bump the version number in adherence to [Semantic Versioning](http://semver.org/) and [Developmental Release](https://peps.python.org/pep-0440/#developmental-releases). + 1. Use the latest supported Python version. Using a [virtual environment](#python-and-friends) is recommended. + 2. In `slack_bolt/version.py` bump the version number in adherence to [Semantic Versioning](http://semver.org/) and [Developmental Release](https://peps.python.org/pep-0440/#developmental-releases). - Example: if the current version is `1.2.3`, a proper development bump would be `1.2.4.dev0` - `.dev` will indicate to pip that this is a [Development Release](https://peps.python.org/pep-0440/#developmental-releases) - Note that the `dev` version can be bumped in development releases: `1.2.4.dev0` -> `1.2.4.dev1` - 2. Build the docs with `./scripts/generate_api_docs.sh`. - 3. Commit with a message including the new version number. For example `1.2.4.dev0` & push the commit to a branch where the development release will live (create it if it does not exist) + 3. Build the docs with `./scripts/generate_api_docs.sh`. + 4. Commit with a message including the new version number. For example `1.2.4.dev0` & push the commit to a branch where the development release will live (create it if it does not exist) 1. `git checkout -b future-release` - 2. `git commit -m 'chore(release): version 1.2.4.dev0'` - 3. `git push -u origin future-release` + 2. `git add --all` (review files with `git status` before committing) + 3. `git commit -m 'chore(release): version 1.2.4.dev0'` + 4. `git push -u origin future-release` 2. Create a new GitHub Release 1. Navigate to the [Releases page](https://github.com/slackapi/bolt-python/releases). 2. Click the "Draft a new release" button. @@ -179,14 +181,16 @@ Before creating a new release, ensure that everything on the `main` branch since the last tag is in a releasable state! At a minimum, [run the tests](#run-all-the-unit-tests). 1. Create the commit for the release - 1. In `slack_bolt/version.py` bump the version number in adherence to [Semantic Versioning](http://semver.org/) and the [Versioning](#versioning-and-tags) section. - 2. Build the docs with `./scripts/generate_api_docs.sh`. - 3. Commit with a message including the new version number. For example `1.2.3` & push the commit to a branch and create a PR to sanity check. + 1. Use the latest supported Python version. Using a [virtual environment](#python-and-friends) is recommended. + 2. In `slack_bolt/version.py` bump the version number in adherence to [Semantic Versioning](http://semver.org/) and the [Versioning](#versioning-and-tags) section. + 3. Build the docs with `./scripts/generate_api_docs.sh`. + 4. Commit with a message including the new version number. For example `1.2.3` & push the commit to a branch and create a PR to sanity check. 1. `git checkout -b 1.2.3-release` - 2. `git commit -m 'chore(release): version 1.2.3'` - 3. `git push -u origin 1.2.3-release` - 4. Add relevant labels to the PR and add the PR to a GitHub Milestone. - 5. Merge in release PR after getting an approval from at least one maintainer. + 2. `git add --all` (review files with `git status` before committing) + 3. `git commit -m 'chore(release): version 1.2.3'` + 4. `git push -u origin 1.2.3-release` + 5. Add relevant labels to the PR and add the PR to a GitHub Milestone. + 6. Merge in release PR after getting an approval from at least one maintainer. 2. Create a new GitHub Release 1. Navigate to the [Releases page](https://github.com/slackapi/bolt-python/releases). 2. Click the "Draft a new release" button. From 9ea1a3bea9ce4f0a7813cf22b714535535c56b04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:00:05 -0500 Subject: [PATCH 166/282] chore(deps): bump actions/setup-python from 6.0.0 to 6.1.0 (#1405) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codecov.yml | 2 +- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .github/workflows/pypi-release.yml | 2 +- .github/workflows/tests.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 02c318a20..ff9669a8c 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -22,7 +22,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index f777996b4..fda0be853 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -20,7 +20,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.python-version }} - name: Run flake8 verification diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 52d59c830..64e600fe2 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -20,7 +20,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.python-version }} - name: Run mypy verification diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 21b472247..11413545a 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Set up Python - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: "3.x" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 42fd58ef6..5de9f35d1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,7 +32,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.python-version }} - name: Install synchronous dependencies From db6a5f679926f6e3d6451ee47bcc36af6ea5dbe6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:49:09 -0500 Subject: [PATCH 167/282] chore(deps): bump mypy from 1.18.2 to 1.19.0 (#1403) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 73a342ca6..f9fbcdac1 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.18.2 +mypy==1.19.0 flake8==7.3.0 black==25.1.0 From 91512bf6f8d6a34d0a84463b54fba3bdd08f207c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:53:43 -0500 Subject: [PATCH 168/282] chore(deps): update pytest-asyncio requirement from <1 to <2 (#1329) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin --- requirements/testing.txt | 2 +- .../socket_mode/mock_socket_mode_server.py | 2 +- .../socket_mode/test_async_aiohttp.py | 14 ++++++------- .../socket_mode/test_async_lazy_listeners.py | 14 ++++++------- .../socket_mode/test_async_websockets.py | 14 ++++++------- tests/adapter_tests_async/test_async_sanic.py | 14 ++++++------- tests/scenario_tests_async/test_app.py | 14 ++++++------- .../test_app_actor_user_token.py | 14 ++++++------- .../scenario_tests_async/test_app_bot_only.py | 14 ++++++------- .../test_app_custom_authorize.py | 14 ++++++------- .../scenario_tests_async/test_app_dispatch.py | 14 ++++++------- .../test_app_installation_store.py | 14 ++++++------- .../test_app_using_methods_in_class.py | 14 ++++++------- .../test_attachment_actions.py | 14 ++++++------- tests/scenario_tests_async/test_authorize.py | 14 ++++++------- .../test_block_actions.py | 14 ++++++------- .../test_block_actions_respond.py | 14 ++++++------- .../test_block_suggestion.py | 14 ++++++------- tests/scenario_tests_async/test_dialogs.py | 14 ++++++------- .../test_error_handler.py | 14 ++++++------- tests/scenario_tests_async/test_events.py | 14 ++++++------- .../test_events_assistant.py | 14 ++++++------- .../test_events_ignore_self.py | 14 ++++++------- .../test_events_org_apps.py | 14 ++++++------- .../test_events_request_verification.py | 14 ++++++------- .../test_events_shared_channels.py | 14 ++++++------- .../test_events_socket_mode.py | 14 ++++++------- .../test_events_token_revocations.py | 14 ++++++------- .../test_events_url_verification.py | 14 ++++++------- tests/scenario_tests_async/test_function.py | 12 +++++------ .../test_installation_store_authorize.py | 14 ++++++------- tests/scenario_tests_async/test_lazy.py | 14 ++++++------- .../test_listener_middleware.py | 14 ++++++------- tests/scenario_tests_async/test_message.py | 14 ++++++------- .../scenario_tests_async/test_message_bot.py | 14 ++++++------- .../test_message_changed.py | 14 ++++++------- .../test_message_deleted.py | 14 ++++++------- .../test_message_file_share.py | 14 ++++++------- .../test_message_thread_broadcast.py | 14 ++++++------- tests/scenario_tests_async/test_middleware.py | 14 ++++++------- tests/scenario_tests_async/test_shortcut.py | 14 ++++++------- .../test_slash_command.py | 14 ++++++------- tests/scenario_tests_async/test_ssl_check.py | 14 ++++++------- .../scenario_tests_async/test_view_closed.py | 14 ++++++------- .../test_view_submission.py | 14 ++++++------- .../test_web_client_customization.py | 14 ++++++------- .../test_workflow_steps.py | 14 ++++++------- .../test_workflow_steps_decorator_simple.py | 15 ++++++-------- ...test_workflow_steps_decorator_with_args.py | 15 ++++++-------- .../authorization/test_async_authorize.py | 14 ++++++------- .../context/test_async_complete.py | 19 ++++++++++-------- .../context/test_async_fail.py | 18 +++++++++-------- .../context/test_async_respond.py | 17 +++++++++------- .../context/test_async_say.py | 20 ++++++++++--------- .../context/test_async_set_status.py | 20 ++++++++++--------- .../test_async_set_suggested_prompts.py | 19 ++++++++++-------- .../test_single_team_authorization.py | 16 +++++++-------- .../test_request_verification.py | 7 ------- .../oauth/test_async_oauth_flow.py | 18 +++++++++-------- .../oauth/test_async_oauth_flow_sqlite3.py | 15 +++++++------- tests/utils.py | 10 ---------- 61 files changed, 377 insertions(+), 478 deletions(-) diff --git a/requirements/testing.txt b/requirements/testing.txt index 7cd7d353a..62fdcca2d 100644 --- a/requirements/testing.txt +++ b/requirements/testing.txt @@ -1,4 +1,4 @@ # pip install -r requirements/testing.txt -r testing_without_asyncio.txt -r async.txt -pytest-asyncio<1; +pytest-asyncio<2; diff --git a/tests/adapter_tests/socket_mode/mock_socket_mode_server.py b/tests/adapter_tests/socket_mode/mock_socket_mode_server.py index 997657368..f59999192 100644 --- a/tests/adapter_tests/socket_mode/mock_socket_mode_server.py +++ b/tests/adapter_tests/socket_mode/mock_socket_mode_server.py @@ -28,8 +28,8 @@ def reset_server_state(): async def health(request: web.Request): wr = web.Response() - await wr.prepare(request) wr.set_status(200) + await wr.prepare(request) return wr async def link(request: web.Request): diff --git a/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py b/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py index 1720f7ec6..e8077f10c 100644 --- a/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py +++ b/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py @@ -9,7 +9,7 @@ setup_mock_web_api_server, cleanup_mock_web_api_server, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env from ...adapter_tests.socket_mode.mock_socket_mode_server import ( start_socket_mode_server, stop_socket_mode_server, @@ -24,16 +24,14 @@ class TestSocketModeAiohttp: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) try: - setup_mock_web_api_server(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server(self) + yield # run the test here finally: + cleanup_mock_web_api_server(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/adapter_tests_async/socket_mode/test_async_lazy_listeners.py b/tests/adapter_tests_async/socket_mode/test_async_lazy_listeners.py index 11268c6a1..9144bd239 100644 --- a/tests/adapter_tests_async/socket_mode/test_async_lazy_listeners.py +++ b/tests/adapter_tests_async/socket_mode/test_async_lazy_listeners.py @@ -9,7 +9,7 @@ setup_mock_web_api_server, cleanup_mock_web_api_server, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env from ...adapter_tests.socket_mode.mock_socket_mode_server import ( start_socket_mode_server, stop_socket_mode_server, @@ -24,16 +24,14 @@ class TestSocketModeAiohttp: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) try: - setup_mock_web_api_server(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server(self) + yield # run the test here finally: + cleanup_mock_web_api_server(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/adapter_tests_async/socket_mode/test_async_websockets.py b/tests/adapter_tests_async/socket_mode/test_async_websockets.py index db2680fc6..84d20b2f9 100644 --- a/tests/adapter_tests_async/socket_mode/test_async_websockets.py +++ b/tests/adapter_tests_async/socket_mode/test_async_websockets.py @@ -9,7 +9,7 @@ setup_mock_web_api_server, cleanup_mock_web_api_server, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env from ...adapter_tests.socket_mode.mock_socket_mode_server import ( start_socket_mode_server, stop_socket_mode_server, @@ -24,16 +24,14 @@ class TestSocketModeWebsockets: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) try: - setup_mock_web_api_server(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server(self) + yield # run the test here finally: + cleanup_mock_web_api_server(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/adapter_tests_async/test_async_sanic.py b/tests/adapter_tests_async/test_async_sanic.py index 316110a87..9a948e3a6 100644 --- a/tests/adapter_tests_async/test_async_sanic.py +++ b/tests/adapter_tests_async/test_async_sanic.py @@ -17,7 +17,7 @@ cleanup_mock_web_api_server, assert_auth_test_count, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestSanic: @@ -34,16 +34,14 @@ class TestSanic: def unique_sanic_app_name() -> str: return f"awesome-slack-app-{str(time()).replace('.', '-')}" - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) try: - setup_mock_web_api_server(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server(self) + yield # run the test here finally: + cleanup_mock_web_api_server(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_app.py b/tests/scenario_tests_async/test_app.py index 8a9512f74..e27dbd3b3 100644 --- a/tests/scenario_tests_async/test_app.py +++ b/tests/scenario_tests_async/test_app.py @@ -19,7 +19,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncApp: @@ -27,16 +27,14 @@ class TestAsyncApp: valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def setup_method(self): diff --git a/tests/scenario_tests_async/test_app_actor_user_token.py b/tests/scenario_tests_async/test_app_actor_user_token.py index 35bda0798..0028096be 100644 --- a/tests/scenario_tests_async/test_app_actor_user_token.py +++ b/tests/scenario_tests_async/test_app_actor_user_token.py @@ -23,7 +23,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestApp: @@ -36,16 +36,14 @@ class TestApp: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_app_bot_only.py b/tests/scenario_tests_async/test_app_bot_only.py index 58e705bac..b350aeb2d 100644 --- a/tests/scenario_tests_async/test_app_bot_only.py +++ b/tests/scenario_tests_async/test_app_bot_only.py @@ -22,7 +22,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAppBotOnly: @@ -35,16 +35,14 @@ class TestAppBotOnly: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_app_custom_authorize.py b/tests/scenario_tests_async/test_app_custom_authorize.py index f1e435f63..2b0252645 100644 --- a/tests/scenario_tests_async/test_app_custom_authorize.py +++ b/tests/scenario_tests_async/test_app_custom_authorize.py @@ -26,7 +26,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAppCustomAuthorize: @@ -39,16 +39,14 @@ class TestAppCustomAuthorize: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_app_dispatch.py b/tests/scenario_tests_async/test_app_dispatch.py index 814d1897a..c483bed19 100644 --- a/tests/scenario_tests_async/test_app_dispatch.py +++ b/tests/scenario_tests_async/test_app_dispatch.py @@ -7,7 +7,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncAppDispatch: @@ -16,16 +16,14 @@ class TestAsyncAppDispatch: mock_api_server_base_url = "http://localhost:8888" web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/scenario_tests_async/test_app_installation_store.py b/tests/scenario_tests_async/test_app_installation_store.py index 053adf52f..16670f1b2 100644 --- a/tests/scenario_tests_async/test_app_installation_store.py +++ b/tests/scenario_tests_async/test_app_installation_store.py @@ -23,7 +23,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestApp: @@ -36,16 +36,14 @@ class TestApp: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_app_using_methods_in_class.py b/tests/scenario_tests_async/test_app_using_methods_in_class.py index a24fe9528..989de511e 100644 --- a/tests/scenario_tests_async/test_app_using_methods_in_class.py +++ b/tests/scenario_tests_async/test_app_using_methods_in_class.py @@ -18,7 +18,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAppUsingMethodsInClass: @@ -31,16 +31,14 @@ class TestAppUsingMethodsInClass: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def test_inspect_behaviors(self): diff --git a/tests/scenario_tests_async/test_attachment_actions.py b/tests/scenario_tests_async/test_attachment_actions.py index c9817dcd7..ea07e6ed0 100644 --- a/tests/scenario_tests_async/test_attachment_actions.py +++ b/tests/scenario_tests_async/test_attachment_actions.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncAttachmentActions: @@ -26,16 +26,14 @@ class TestAsyncAttachmentActions: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_authorize.py b/tests/scenario_tests_async/test_authorize.py index 2cd18531b..9d6e3d4af 100644 --- a/tests/scenario_tests_async/test_authorize.py +++ b/tests/scenario_tests_async/test_authorize.py @@ -16,7 +16,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env valid_token = "xoxb-valid" valid_user_token = "xoxp-valid" @@ -60,16 +60,14 @@ class TestAsyncAuthorize: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_block_actions.py b/tests/scenario_tests_async/test_block_actions.py index 716a5a80b..6441b2e4b 100644 --- a/tests/scenario_tests_async/test_block_actions.py +++ b/tests/scenario_tests_async/test_block_actions.py @@ -16,7 +16,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncBlockActions: @@ -29,16 +29,14 @@ class TestAsyncBlockActions: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_block_actions_respond.py b/tests/scenario_tests_async/test_block_actions_respond.py index 6d8f80884..b95e1bbf3 100644 --- a/tests/scenario_tests_async/test_block_actions_respond.py +++ b/tests/scenario_tests_async/test_block_actions_respond.py @@ -8,7 +8,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncBlockActionsRespond: @@ -20,16 +20,14 @@ class TestAsyncBlockActionsRespond: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/scenario_tests_async/test_block_suggestion.py b/tests/scenario_tests_async/test_block_suggestion.py index fab3b48ec..2450957f4 100644 --- a/tests/scenario_tests_async/test_block_suggestion.py +++ b/tests/scenario_tests_async/test_block_suggestion.py @@ -14,7 +14,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncBlockSuggestion: @@ -27,16 +27,14 @@ class TestAsyncBlockSuggestion: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_dialogs.py b/tests/scenario_tests_async/test_dialogs.py index 1a3573d00..110fca45a 100644 --- a/tests/scenario_tests_async/test_dialogs.py +++ b/tests/scenario_tests_async/test_dialogs.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncAttachmentActions: @@ -26,16 +26,14 @@ class TestAsyncAttachmentActions: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_error_handler.py b/tests/scenario_tests_async/test_error_handler.py index 00cf5c07d..fb0b9ddae 100644 --- a/tests/scenario_tests_async/test_error_handler.py +++ b/tests/scenario_tests_async/test_error_handler.py @@ -16,7 +16,7 @@ cleanup_mock_web_api_server, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncErrorHandler: @@ -29,16 +29,14 @@ class TestAsyncErrorHandler: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) # ---------------- diff --git a/tests/scenario_tests_async/test_events.py b/tests/scenario_tests_async/test_events.py index 774d526ee..0cdaa0fac 100644 --- a/tests/scenario_tests_async/test_events.py +++ b/tests/scenario_tests_async/test_events.py @@ -19,7 +19,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncEvents: @@ -32,16 +32,14 @@ class TestAsyncEvents: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index ac2c734c5..b131b4e38 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -14,7 +14,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncEventsAssistant: @@ -25,16 +25,14 @@ class TestAsyncEventsAssistant: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/scenario_tests_async/test_events_ignore_self.py b/tests/scenario_tests_async/test_events_ignore_self.py index 14fcb509a..7ec9d0cce 100644 --- a/tests/scenario_tests_async/test_events_ignore_self.py +++ b/tests/scenario_tests_async/test_events_ignore_self.py @@ -11,7 +11,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncEventsIgnoreSelf: @@ -22,16 +22,14 @@ class TestAsyncEventsIgnoreSelf: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/scenario_tests_async/test_events_org_apps.py b/tests/scenario_tests_async/test_events_org_apps.py index 187c59b77..e3706d9c6 100644 --- a/tests/scenario_tests_async/test_events_org_apps.py +++ b/tests/scenario_tests_async/test_events_org_apps.py @@ -20,7 +20,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env valid_token = "xoxb-valid" @@ -57,16 +57,14 @@ class TestAsyncOrgApps: signature_verifier = SignatureVerifier(signing_secret) web_client = AsyncWebClient(token=None, base_url=mock_api_server_base_url) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_events_request_verification.py b/tests/scenario_tests_async/test_events_request_verification.py index f314852b0..51ccfcd98 100644 --- a/tests/scenario_tests_async/test_events_request_verification.py +++ b/tests/scenario_tests_async/test_events_request_verification.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncEventsRequestVerification: @@ -23,16 +23,14 @@ class TestAsyncEventsRequestVerification: signature_verifier = SignatureVerifier(signing_secret) web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_events_shared_channels.py b/tests/scenario_tests_async/test_events_shared_channels.py index 0112de808..ca43f979a 100644 --- a/tests/scenario_tests_async/test_events_shared_channels.py +++ b/tests/scenario_tests_async/test_events_shared_channels.py @@ -17,7 +17,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env valid_token = "xoxb-valid" @@ -39,16 +39,14 @@ class TestAsyncEventsSharedChannels: signature_verifier = SignatureVerifier(signing_secret) web_client = AsyncWebClient(token=None, base_url=mock_api_server_base_url) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_events_socket_mode.py b/tests/scenario_tests_async/test_events_socket_mode.py index 75ab349ad..e3d3fc98f 100644 --- a/tests/scenario_tests_async/test_events_socket_mode.py +++ b/tests/scenario_tests_async/test_events_socket_mode.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncEvents: @@ -24,16 +24,14 @@ class TestAsyncEvents: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def build_valid_app_mention_request(self) -> AsyncBoltRequest: diff --git a/tests/scenario_tests_async/test_events_token_revocations.py b/tests/scenario_tests_async/test_events_token_revocations.py index ecadbc53f..0c079eede 100644 --- a/tests/scenario_tests_async/test_events_token_revocations.py +++ b/tests/scenario_tests_async/test_events_token_revocations.py @@ -18,7 +18,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env valid_token = "xoxb-valid" @@ -49,16 +49,14 @@ class TestEventsTokenRevocations: signature_verifier = SignatureVerifier(signing_secret) web_client = AsyncWebClient(token=None, base_url=mock_api_server_base_url) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_events_url_verification.py b/tests/scenario_tests_async/test_events_url_verification.py index c095791cd..123fd3cce 100644 --- a/tests/scenario_tests_async/test_events_url_verification.py +++ b/tests/scenario_tests_async/test_events_url_verification.py @@ -12,7 +12,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncEventsUrlVerification: @@ -22,16 +22,14 @@ class TestAsyncEventsUrlVerification: signature_verifier = SignatureVerifier(signing_secret) web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_function.py b/tests/scenario_tests_async/test_function.py index 142cc1d6c..abf3ffb48 100644 --- a/tests/scenario_tests_async/test_function.py +++ b/tests/scenario_tests_async/test_function.py @@ -33,16 +33,14 @@ class TestAsyncFunction: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = asyncio.get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_installation_store_authorize.py b/tests/scenario_tests_async/test_installation_store_authorize.py index ad0b24250..bef7d39e0 100644 --- a/tests/scenario_tests_async/test_installation_store_authorize.py +++ b/tests/scenario_tests_async/test_installation_store_authorize.py @@ -18,7 +18,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env valid_token = "xoxb-valid" valid_user_token = "xoxp-valid" @@ -63,16 +63,14 @@ class TestAsyncInstallationStoreAuthorize: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_lazy.py b/tests/scenario_tests_async/test_lazy.py index 02a2bd0fa..7bf780e08 100644 --- a/tests/scenario_tests_async/test_lazy.py +++ b/tests/scenario_tests_async/test_lazy.py @@ -14,7 +14,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncLazy: @@ -27,16 +27,14 @@ class TestAsyncLazy: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) # ---------------- diff --git a/tests/scenario_tests_async/test_listener_middleware.py b/tests/scenario_tests_async/test_listener_middleware.py index 4e6419e96..1b3d9b17d 100644 --- a/tests/scenario_tests_async/test_listener_middleware.py +++ b/tests/scenario_tests_async/test_listener_middleware.py @@ -12,7 +12,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncListenerMiddleware: @@ -25,16 +25,14 @@ class TestAsyncListenerMiddleware: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) body = { diff --git a/tests/scenario_tests_async/test_message.py b/tests/scenario_tests_async/test_message.py index cc0fbb8ec..374760323 100644 --- a/tests/scenario_tests_async/test_message.py +++ b/tests/scenario_tests_async/test_message.py @@ -16,7 +16,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncMessage: @@ -29,16 +29,14 @@ class TestAsyncMessage: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_message_bot.py b/tests/scenario_tests_async/test_message_bot.py index 8e5e28c87..50f29271c 100644 --- a/tests/scenario_tests_async/test_message_bot.py +++ b/tests/scenario_tests_async/test_message_bot.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncMessage: @@ -26,16 +26,14 @@ class TestAsyncMessage: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_message_changed.py b/tests/scenario_tests_async/test_message_changed.py index 15658d636..66468f14a 100644 --- a/tests/scenario_tests_async/test_message_changed.py +++ b/tests/scenario_tests_async/test_message_changed.py @@ -11,7 +11,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncMessageChanged: @@ -24,16 +24,14 @@ class TestAsyncMessageChanged: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_message_deleted.py b/tests/scenario_tests_async/test_message_deleted.py index 09f669d48..d5b6ba80c 100644 --- a/tests/scenario_tests_async/test_message_deleted.py +++ b/tests/scenario_tests_async/test_message_deleted.py @@ -11,7 +11,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncMessageDeleted: @@ -24,16 +24,14 @@ class TestAsyncMessageDeleted: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_message_file_share.py b/tests/scenario_tests_async/test_message_file_share.py index 6f55957ac..f156d9286 100644 --- a/tests/scenario_tests_async/test_message_file_share.py +++ b/tests/scenario_tests_async/test_message_file_share.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncMessageFileShare: @@ -26,16 +26,14 @@ class TestAsyncMessageFileShare: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_message_thread_broadcast.py b/tests/scenario_tests_async/test_message_thread_broadcast.py index c3ac6dd01..c15bbdc99 100644 --- a/tests/scenario_tests_async/test_message_thread_broadcast.py +++ b/tests/scenario_tests_async/test_message_thread_broadcast.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncMessageThreadBroadcast: @@ -26,16 +26,14 @@ class TestAsyncMessageThreadBroadcast: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_middleware.py b/tests/scenario_tests_async/test_middleware.py index 6272f17e4..f8dfe9623 100644 --- a/tests/scenario_tests_async/test_middleware.py +++ b/tests/scenario_tests_async/test_middleware.py @@ -20,7 +20,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env # Note that async middleware system does not support instance methods n a class. @@ -34,16 +34,14 @@ class TestAsyncMiddleware: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def build_request(self) -> AsyncBoltRequest: diff --git a/tests/scenario_tests_async/test_shortcut.py b/tests/scenario_tests_async/test_shortcut.py index 9ad4b2b03..bd3c595eb 100644 --- a/tests/scenario_tests_async/test_shortcut.py +++ b/tests/scenario_tests_async/test_shortcut.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncShortcut: @@ -26,16 +26,14 @@ class TestAsyncShortcut: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_slash_command.py b/tests/scenario_tests_async/test_slash_command.py index 6c6d9ef88..1ac02bce7 100644 --- a/tests/scenario_tests_async/test_slash_command.py +++ b/tests/scenario_tests_async/test_slash_command.py @@ -12,7 +12,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncSlashCommand: @@ -25,16 +25,14 @@ class TestAsyncSlashCommand: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_ssl_check.py b/tests/scenario_tests_async/test_ssl_check.py index 894bf82c2..ef32bc0dd 100644 --- a/tests/scenario_tests_async/test_ssl_check.py +++ b/tests/scenario_tests_async/test_ssl_check.py @@ -10,7 +10,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncSSLCheck: @@ -23,16 +23,14 @@ class TestAsyncSSLCheck: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_view_closed.py b/tests/scenario_tests_async/test_view_closed.py index a633b0d3b..1b86d22db 100644 --- a/tests/scenario_tests_async/test_view_closed.py +++ b/tests/scenario_tests_async/test_view_closed.py @@ -13,7 +13,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncViewClosed: @@ -26,16 +26,14 @@ class TestAsyncViewClosed: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_view_submission.py b/tests/scenario_tests_async/test_view_submission.py index efb1c25f5..49a6e8fc5 100644 --- a/tests/scenario_tests_async/test_view_submission.py +++ b/tests/scenario_tests_async/test_view_submission.py @@ -13,7 +13,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env body = { @@ -198,16 +198,14 @@ class TestAsyncViewSubmission: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_web_client_customization.py b/tests/scenario_tests_async/test_web_client_customization.py index c9b42a617..8ed78b2c3 100644 --- a/tests/scenario_tests_async/test_web_client_customization.py +++ b/tests/scenario_tests_async/test_web_client_customization.py @@ -16,7 +16,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestWebClientCustomization: @@ -30,16 +30,14 @@ class TestWebClientCustomization: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_workflow_steps.py b/tests/scenario_tests_async/test_workflow_steps.py index ea9766361..a99dedbfe 100644 --- a/tests/scenario_tests_async/test_workflow_steps.py +++ b/tests/scenario_tests_async/test_workflow_steps.py @@ -21,7 +21,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncWorkflowSteps: @@ -34,16 +34,14 @@ class TestAsyncWorkflowSteps: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_workflow_steps_decorator_simple.py b/tests/scenario_tests_async/test_workflow_steps_decorator_simple.py index f404bf947..1224949ae 100644 --- a/tests/scenario_tests_async/test_workflow_steps_decorator_simple.py +++ b/tests/scenario_tests_async/test_workflow_steps_decorator_simple.py @@ -21,7 +21,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncWorkflowStepsDecorator: @@ -34,19 +34,16 @@ class TestAsyncWorkflowStepsDecorator: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) self.app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret) self.app.step(copy_review_step) - - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/scenario_tests_async/test_workflow_steps_decorator_with_args.py b/tests/scenario_tests_async/test_workflow_steps_decorator_with_args.py index 02d0ad8c7..53bec512d 100644 --- a/tests/scenario_tests_async/test_workflow_steps_decorator_with_args.py +++ b/tests/scenario_tests_async/test_workflow_steps_decorator_with_args.py @@ -22,7 +22,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncWorkflowStepsDecorator: @@ -35,19 +35,16 @@ class TestAsyncWorkflowStepsDecorator: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) self.app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret) self.app.step(copy_review_step) - - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) def generate_signature(self, body: str, timestamp: str): diff --git a/tests/slack_bolt_async/authorization/test_async_authorize.py b/tests/slack_bolt_async/authorization/test_async_authorize.py index f978ebfa7..d98a1062f 100644 --- a/tests/slack_bolt_async/authorization/test_async_authorize.py +++ b/tests/slack_bolt_async/authorization/test_async_authorize.py @@ -21,7 +21,7 @@ assert_auth_test_count_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncAuthorize: @@ -30,16 +30,14 @@ class TestAsyncAuthorize: base_url=mock_api_server_base_url, ) - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/slack_bolt_async/context/test_async_complete.py b/tests/slack_bolt_async/context/test_async_complete.py index b2a464f83..4277d4218 100644 --- a/tests/slack_bolt_async/context/test_async_complete.py +++ b/tests/slack_bolt_async/context/test_async_complete.py @@ -7,20 +7,23 @@ setup_mock_web_api_server, cleanup_mock_web_api_server, ) +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncComplete: - @pytest.fixture - def event_loop(self): + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() setup_mock_web_api_server(self) valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" - self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - - loop = asyncio.get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server(self) + try: + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + yield # run the test here + finally: + cleanup_mock_web_api_server(self) + restore_os_env(old_os_env) @pytest.mark.asyncio async def test_complete(self): diff --git a/tests/slack_bolt_async/context/test_async_fail.py b/tests/slack_bolt_async/context/test_async_fail.py index d4708927f..d344a6c95 100644 --- a/tests/slack_bolt_async/context/test_async_fail.py +++ b/tests/slack_bolt_async/context/test_async_fail.py @@ -7,20 +7,22 @@ setup_mock_web_api_server, cleanup_mock_web_api_server, ) +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncFail: - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() setup_mock_web_api_server(self) valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" - self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - - loop = asyncio.get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server(self) + try: + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + yield # run the test here + finally: + cleanup_mock_web_api_server(self) + restore_os_env(old_os_env) @pytest.mark.asyncio async def test_fail(self): diff --git a/tests/slack_bolt_async/context/test_async_respond.py b/tests/slack_bolt_async/context/test_async_respond.py index eba44d6a0..b47ef1056 100644 --- a/tests/slack_bolt_async/context/test_async_respond.py +++ b/tests/slack_bolt_async/context/test_async_respond.py @@ -1,6 +1,6 @@ import pytest -from tests.utils import get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env from slack_bolt.context.respond.async_respond import AsyncRespond from tests.mock_web_api_server import ( cleanup_mock_web_api_server_async, @@ -9,13 +9,16 @@ class TestAsyncRespond: - @pytest.fixture - def event_loop(self): + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + try: + yield # run the test here + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) @pytest.mark.asyncio async def test_respond(self): diff --git a/tests/slack_bolt_async/context/test_async_say.py b/tests/slack_bolt_async/context/test_async_say.py index efa90febc..d8d63ae8a 100644 --- a/tests/slack_bolt_async/context/test_async_say.py +++ b/tests/slack_bolt_async/context/test_async_say.py @@ -4,21 +4,23 @@ from slack_bolt.context.say.async_say import AsyncSay from tests.mock_web_api_server import cleanup_mock_web_api_server_async, setup_mock_web_api_server_async -from tests.utils import get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncSay: - @pytest.fixture - def event_loop(self): + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() setup_mock_web_api_server_async(self) valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" - self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + try: + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + yield # run the test here + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) @pytest.mark.asyncio async def test_say(self): diff --git a/tests/slack_bolt_async/context/test_async_set_status.py b/tests/slack_bolt_async/context/test_async_set_status.py index 8df34171f..e785ff89e 100644 --- a/tests/slack_bolt_async/context/test_async_set_status.py +++ b/tests/slack_bolt_async/context/test_async_set_status.py @@ -4,21 +4,23 @@ from slack_bolt.context.set_status.async_set_status import AsyncSetStatus from tests.mock_web_api_server import cleanup_mock_web_api_server_async, setup_mock_web_api_server_async -from tests.utils import get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncSetStatus: - @pytest.fixture - def event_loop(self): + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() setup_mock_web_api_server_async(self) valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" - self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + try: + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + yield # run the test here + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) @pytest.mark.asyncio async def test_set_status(self): diff --git a/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py b/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py index 70a24efcb..2a09434a8 100644 --- a/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py +++ b/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py @@ -6,20 +6,23 @@ from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server +from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncSetSuggestedPrompts: - @pytest.fixture - def event_loop(self): + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() setup_mock_web_api_server(self) valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" - self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - - loop = asyncio.get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server(self) + try: + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + yield # run the test here + finally: + cleanup_mock_web_api_server(self) + restore_os_env(old_os_env) @pytest.mark.asyncio async def test_set_suggested_prompts(self): diff --git a/tests/slack_bolt_async/middleware/authorization/test_single_team_authorization.py b/tests/slack_bolt_async/middleware/authorization/test_single_team_authorization.py index e90eae5c8..0ddb6281d 100644 --- a/tests/slack_bolt_async/middleware/authorization/test_single_team_authorization.py +++ b/tests/slack_bolt_async/middleware/authorization/test_single_team_authorization.py @@ -11,7 +11,7 @@ cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, ) -from tests.utils import remove_os_env_temporarily, restore_os_env, get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env async def next(): @@ -19,18 +19,16 @@ async def next(): class TestSingleTeamAuthorization: - mock_api_server_base_url = "http://localhost:8888" - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) try: - setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + self.mock_api_server_base_url = "http://localhost:8888" + yield # run the test here finally: + cleanup_mock_web_api_server_async(self) restore_os_env(old_os_env) @pytest.mark.asyncio diff --git a/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py b/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py index 0b05079a9..c097dd146 100644 --- a/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py +++ b/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py @@ -1,7 +1,6 @@ from time import time import pytest -from tests.utils import get_event_loop from slack_sdk.signature import SignatureVerifier from slack_bolt.middleware.request_verification.async_request_verification import ( @@ -32,12 +31,6 @@ def build_headers(self, timestamp: str, body: str): "x-slack-request-timestamp": [timestamp], } - @pytest.fixture - def event_loop(self): - loop = get_event_loop() - yield loop - loop.close() - @pytest.mark.asyncio async def test_valid(self): middleware = AsyncRequestVerification(signing_secret="secret") diff --git a/tests/slack_bolt_async/oauth/test_async_oauth_flow.py b/tests/slack_bolt_async/oauth/test_async_oauth_flow.py index 1a7b89552..5714e1a6a 100644 --- a/tests/slack_bolt_async/oauth/test_async_oauth_flow.py +++ b/tests/slack_bolt_async/oauth/test_async_oauth_flow.py @@ -3,7 +3,7 @@ from urllib.parse import quote import pytest -from tests.utils import get_event_loop +from tests.utils import remove_os_env_temporarily, restore_os_env from slack_sdk.oauth.installation_store import FileInstallationStore from slack_sdk.oauth.state_store import FileOAuthStateStore from slack_sdk.oauth.state_store.async_state_store import AsyncOAuthStateStore @@ -30,15 +30,17 @@ class TestAsyncOAuthFlow: - mock_api_server_base_url = "http://localhost:8888" - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + try: + self.mock_api_server_base_url = "http://localhost:8888" + yield # run the test here + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) def next(self): pass diff --git a/tests/slack_bolt_async/oauth/test_async_oauth_flow_sqlite3.py b/tests/slack_bolt_async/oauth/test_async_oauth_flow_sqlite3.py index 238ce8873..00300929f 100644 --- a/tests/slack_bolt_async/oauth/test_async_oauth_flow_sqlite3.py +++ b/tests/slack_bolt_async/oauth/test_async_oauth_flow_sqlite3.py @@ -1,7 +1,6 @@ import pytest from slack_sdk.web.async_client import AsyncWebClient -from tests.utils import get_event_loop from slack_bolt import BoltResponse from slack_bolt.oauth.async_callback_options import ( AsyncFailureArgs, @@ -17,15 +16,15 @@ class TestAsyncOAuthFlowSQLite3: - mock_api_server_base_url = "http://localhost:8888" - @pytest.fixture - def event_loop(self): + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): setup_mock_web_api_server_async(self) - loop = get_event_loop() - yield loop - loop.close() - cleanup_mock_web_api_server_async(self) + try: + self.mock_api_server_base_url = "http://localhost:8888" + yield # run the test here + finally: + cleanup_mock_web_api_server_async(self) def next(self): pass diff --git a/tests/utils.py b/tests/utils.py index eb9759c5d..e06d0f861 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -13,13 +13,3 @@ def remove_os_env_temporarily() -> dict: def restore_os_env(old_env: dict) -> None: os.environ.update(old_env) - - -def get_event_loop(): - try: - return asyncio.get_event_loop() - except RuntimeError as ex: - if "There is no current event loop in thread" in str(ex): - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - return loop From 2e042283dd1fac79a797a9e274fbea29c25be4c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 10:12:40 -0500 Subject: [PATCH 169/282] chore(deps): bump actions/checkout from 5.0.0 to 6.0.0 (#1404) --- .github/workflows/codecov.yml | 2 +- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .github/workflows/pypi-release.yml | 2 +- .github/workflows/tests.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index ff9669a8c..f93864817 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -18,7 +18,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index fda0be853..b0602d60a 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 64e600fe2..650c8f9bd 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 11413545a..80fac6d8c 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: ref: ${{ github.event.release.tag_name || github.ref }} persist-credentials: false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5de9f35d1..6de00d7da 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,7 +28,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} From 46f0b9506685c347b475c6ceb19ccee9520ed810 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 13:02:51 -0500 Subject: [PATCH 170/282] chore(deps): update cheroot requirement from <11 to <12 (#1380) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Brooks Co-authored-by: William Bergamin --- requirements/adapter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 3cefd621d..b2097bcdb 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -4,7 +4,7 @@ boto3<=2 bottle>=0.12,<1 chalice>=1.28,<2; -cheroot<11 # https://github.com/slackapi/bolt-python/issues/1374 +cheroot<12 CherryPy>=18,<19 Django>=3,<6 falcon>=2,<5; python_version<"3.11" From 91de836bd920cb392ca289af0c0923c859433e39 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:31:52 -0800 Subject: [PATCH 171/282] docs: updates old links throughout (#1409) --- docs/english/concepts/ai-apps.md | 6 +++--- docs/english/concepts/message-sending.md | 6 +++--- docs/japanese/concepts/select-menu-options.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/english/concepts/ai-apps.md b/docs/english/concepts/ai-apps.md index 44bd08df1..3b057bc7e 100644 --- a/docs/english/concepts/ai-apps.md +++ b/docs/english/concepts/ai-apps.md @@ -337,9 +337,9 @@ See the [_Adding and handling feedback_](#adding-and-handling-feedback) section Three Web API methods work together to provide users a text streaming experience: -* the [`chat.startStream`](/reference/methods/chat.startstream) method starts the text stream, -* the [`chat.appendStream`](/reference/methods/chat.appendstream) method appends text to the stream, and -* the [`chat.stopStream`](/reference/methods/chat.stopstream) method stops it. +* the [`chat.startStream`](/reference/methods/chat.startStream) method starts the text stream, +* the [`chat.appendStream`](/reference/methods/chat.appendStream) method appends text to the stream, and +* the [`chat.stopStream`](/reference/methods/chat.stopStream) method stops it. Since you're using Bolt for Python, built upon the Python Slack SDK, you can use the [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) utility to streamline all three aspects of streaming in your app's messages. diff --git a/docs/english/concepts/message-sending.md b/docs/english/concepts/message-sending.md index 9741bb396..87c433129 100644 --- a/docs/english/concepts/message-sending.md +++ b/docs/english/concepts/message-sending.md @@ -45,9 +45,9 @@ def show_datepicker(event, say): You can have your app's messages stream in to replicate conventional AI chatbot behavior. This is done through three Web API methods: -* [`chat_startStream`](/reference/methods/chat.startstream) -* [`chat_appendStream`](/reference/methods/chat.appendstream) -* [`chat_stopStream`](/reference/methods/chat.stopstream) +* [`chat_startStream`](/reference/methods/chat.startStream) +* [`chat_appendStream`](/reference/methods/chat.appendStream) +* [`chat_stopStream`](/reference/methods/chat.stopStream) The Python Slack SDK provides a [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility to streamline calling these methods. Here's an excerpt from our [Assistant template app](https://github.com/slack-samples/bolt-python-assistant-template): diff --git a/docs/japanese/concepts/select-menu-options.md b/docs/japanese/concepts/select-menu-options.md index 1c2d41c58..2c12af623 100644 --- a/docs/japanese/concepts/select-menu-options.md +++ b/docs/japanese/concepts/select-menu-options.md @@ -2,7 +2,7 @@ `options()` メソッドは、Slack からのオプション(セレクトメニュー内の動的な選択肢)をリクエストするペイロードをリッスンします。 [`action()` と同様に](/tools/bolt-python/concepts/actions)、文字列型の `action_id` または制約付きオブジェクトが必要です。 -外部データソースを使って選択メニューをロードするためには、末部に `/slack/events` が付加された URL を Options Load URL として予め設定しておく必要があります。 +外部データソースを使って選択メニューをロードするためには、末部に `/slack/events` が付加された URL を Options Load URL として予め設定しておく必要があります。 `external_select` メニューでは `action_id` を指定することをおすすめしています。ただし、ダイアログを利用している場合、ダイアログが Block Kit に対応していないため、`callback_id` をフィルタリングするための制約オブジェクトを使用する必要があります。 From f02f8c6330e70e0afd9253a59ab8410237b9aab7 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:55:49 -0800 Subject: [PATCH 172/282] docs: updates outmoded links and standardizes markdown links (#1410) --- docs/english/building-an-app.md | 4 ++-- docs/english/getting-started.md | 2 +- docs/english/legacy/steps-from-apps.md | 10 +++------ docs/japanese/concepts/acknowledge.md | 2 +- docs/japanese/concepts/actions.md | 6 ++++-- docs/japanese/concepts/adapters.md | 6 +++--- docs/japanese/concepts/assistant.md | 2 +- docs/japanese/concepts/async.md | 4 ++-- docs/japanese/concepts/commands.md | 2 +- docs/japanese/concepts/event-listening.md | 2 +- docs/japanese/concepts/global-middleware.md | 2 +- docs/japanese/concepts/listener-middleware.md | 2 +- docs/japanese/concepts/message-listening.md | 2 +- docs/japanese/concepts/message-sending.md | 2 +- docs/japanese/concepts/opening-modals.md | 6 +++--- docs/japanese/concepts/select-menu-options.md | 2 +- docs/japanese/concepts/shortcuts.md | 2 +- .../concepts/updating-pushing-views.md | 8 +++---- docs/japanese/concepts/view-submissions.md | 10 ++++----- docs/japanese/concepts/web-api.md | 2 +- docs/japanese/getting-started.md | 4 ++-- docs/japanese/legacy/steps-from-apps.md | 21 +++++++------------ 22 files changed, 48 insertions(+), 55 deletions(-) diff --git a/docs/english/building-an-app.md b/docs/english/building-an-app.md index ee0dac967..bde340961 100644 --- a/docs/english/building-an-app.md +++ b/docs/english/building-an-app.md @@ -55,7 +55,7 @@ We're going to use bot and app-level tokens for this guide. :::tip[Not sharing is sometimes caring] -Treat your tokens like passwords and [keep them safe](/authentication/best-practices-for-security). Your app uses tokens to post and retrieve information from Slack workspaces. +Treat your tokens like passwords and [keep them safe](/security). Your app uses tokens to post and retrieve information from Slack workspaces. ::: @@ -103,7 +103,7 @@ $ export SLACK_APP_TOKEN= :::warning[Keep it secret. Keep it safe.] -Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](/authentication/best-practices-for-security). +Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](/security). ::: diff --git a/docs/english/getting-started.md b/docs/english/getting-started.md index cc428a93a..934dd3bae 100644 --- a/docs/english/getting-started.md +++ b/docs/english/getting-started.md @@ -147,7 +147,7 @@ The above command works on Linux and macOS but [similar commands are available o :::warning[Keep it secret. Keep it safe.] -Treat your tokens like a password and [keep it safe](/authentication/best-practices-for-security). Your app uses these to retrieve and send information to Slack. +Treat your tokens like a password and [keep it safe](/security). Your app uses these to retrieve and send information to Slack. ::: diff --git a/docs/english/legacy/steps-from-apps.md b/docs/english/legacy/steps-from-apps.md index 03b9fa8ff..bced20f9e 100644 --- a/docs/english/legacy/steps-from-apps.md +++ b/docs/english/legacy/steps-from-apps.md @@ -68,14 +68,12 @@ app.step(ws) ## Adding or editing steps from apps -When a builder adds (or later edits) your step in their workflow, your app will receive a [`workflow_step_edit` event](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step_edit-payload). The `edit` callback in your `WorkflowStep` configuration will be run when this event is received. +When a builder adds (or later edits) your step in their workflow, your app will receive a `workflow_step_edit` event. The `edit` callback in your `WorkflowStep` configuration will be run when this event is received. -Whether a builder is adding or editing a step, you need to send them a [step from app configuration modal](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object). This modal is where step-specific settings are chosen, and it has more restrictions than typical modals—most notably, it cannot include `title`, `submit`, or `close` properties. By default, the configuration modal's `callback_id` will be the same as the step from app. +Whether a builder is adding or editing a step, you need to send them a step from app configuration modal. This modal is where step-specific settings are chosen, and it has more restrictions than typical modals—most notably, it cannot include `title`, `submit`, or `close` properties. By default, the configuration modal's `callback_id` will be the same as the step from app. Within the `edit` callback, the `configure()` utility can be used to easily open your step's configuration modal by passing in the view's blocks with the corresponding `blocks` argument. To disable saving the configuration before certain conditions are met, you can also pass in `submit_disabled` with a value of `True`. -To learn more about opening configuration modals, [read the documentation](/legacy/legacy-steps-from-apps/). - Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ```python @@ -126,8 +124,6 @@ Within the `save` callback, the `update()` method can be used to save the builde - `step_name` overrides the default Step name - `step_image_url` overrides the default Step image -To learn more about how to structure these parameters, [read the documentation](/legacy/legacy-steps-from-apps/). - Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ```python @@ -167,7 +163,7 @@ app.step(ws) ## Executing steps from apps -When your step from app is executed by an end user, your app will receive a [`workflow_step_execute` event](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object). The `execute` callback in your `WorkflowStep` configuration will be run when this event is received. +When your step from app is executed by an end user, your app will receive a `workflow_step_execute` event. The `execute` callback in your `WorkflowStep` configuration will be run when this event is received. Using the `inputs` from the `save` callback, this is where you can make third-party API calls, save information to a database, update the user's Home tab, or decide the outputs that will be available to subsequent steps from apps by mapping values to the `outputs` object. diff --git a/docs/japanese/concepts/acknowledge.md b/docs/japanese/concepts/acknowledge.md index 2b3756009..36ba6cba4 100644 --- a/docs/japanese/concepts/acknowledge.md +++ b/docs/japanese/concepts/acknowledge.md @@ -8,7 +8,7 @@ FaaS / serverless 環境を使う場合、 `ack()` するタイミングが異なります。 これに関する詳細は [Lazy listeners (FaaS)](/tools/bolt-python/concepts/lazy-listeners) を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # 外部データを使用する選択メニューオプションに応答するサンプル @app.options("menu_selection") diff --git a/docs/japanese/concepts/actions.md b/docs/japanese/concepts/actions.md index 60019ebb7..8f1d1180e 100644 --- a/docs/japanese/concepts/actions.md +++ b/docs/japanese/concepts/actions.md @@ -8,7 +8,8 @@ Bolt アプリは `action` メソッドを用いて、ボタンのクリック `action()` を使ったすべての例で `ack()` が使用されていることに注目してください。アクションのリスナー内では、Slack からのリクエストを受信したことを確認するために、`ack()` 関数を呼び出す必要があります。これについては、[リクエストの確認](/tools/bolt-python/concepts/acknowledge)セクションで説明しています。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 + ```python # 'approve_button' という action_id のブロックエレメントがトリガーされるたびに、このリスナーが呼び出させれる @app.action("approve_button") @@ -45,7 +46,8 @@ def update_message(ack, body, client): 2 つ目は、`respond()` を使用する方法です。これは、アクションに関連づけられた `response_url` を使ったメッセージ送信を行うためのユーティリティです。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 + ```python # 'approve_button' という action_id のインタラクティブコンポーネントがトリガーされると、このリスナーが呼ばれる @app.action("approve_button") diff --git a/docs/japanese/concepts/adapters.md b/docs/japanese/concepts/adapters.md index a58ed34a2..6ed804c26 100644 --- a/docs/japanese/concepts/adapters.md +++ b/docs/japanese/concepts/adapters.md @@ -1,12 +1,12 @@ # アダプター -アダプターは Slack から届く受信リクエストの受付とパーズを担当し、それらのリクエストを `BoltRequest` の形式に変換して Bolt アプリに引き渡します。 +アダプターは Slack から届く受信リクエストの受付とパーズを担当し、それらのリクエストを [`BoltRequest`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/request/request.py) の形式に変換して Bolt アプリに引き渡します。 -デフォルトでは、Bolt の組み込みの `HTTPServer` アダプターが使われます。このアダプターは、ローカルで開発するのには問題がありませんが、本番環境での利用は推奨されていません。Bolt for Python には複数の組み込みのアダプターが用意されており、必要に応じてインポートしてアプリで使用することができます。組み込みのアダプターは Flask、Django、Starlette をはじめとする様々な人気の Python フレームワークをサポートしています。これらのアダプターは、あなたが選択した本番環境で利用可能な Webサーバーとともに利用することができます。 +デフォルトでは、Bolt の組み込みの [`HTTPServer`](https://docs.python.org/3/library/http.server.html) アダプターが使われます。このアダプターは、ローカルで開発するのには問題がありませんが、**本番環境での利用は推奨されていません**。Bolt for Python には複数の組み込みのアダプターが用意されており、必要に応じてインポートしてアプリで使用することができます。組み込みのアダプターは Flask、Django、Starlette をはじめとする様々な人気の Python フレームワークをサポートしています。これらのアダプターは、あなたが選択した本番環境で利用可能な Webサーバーとともに利用することができます。 アダプターを使用するには、任意のフレームワークを使ってアプリを開発し、そのコードに対応するアダプターをインポートします。その後、アダプターのインスタンスを初期化して、受信リクエストの受付とパーズを行う関数を呼び出します。 -すべてのアダプターの一覧と、設定や使い方のサンプルは、リポジトリの `examples` フォルダをご覧ください。 +すべてのアダプターの一覧と、設定や使い方のサンプルは、リポジトリの [`examples` フォルダ](https://github.com/slackapi/bolt-python/tree/main/examples)をご覧ください。 ```python from slack_bolt import App diff --git a/docs/japanese/concepts/assistant.md b/docs/japanese/concepts/assistant.md index e819f5361..664108607 100644 --- a/docs/japanese/concepts/assistant.md +++ b/docs/japanese/concepts/assistant.md @@ -68,7 +68,7 @@ def respond_in_assistant_thread( app.use(assistant) ``` -リスナーに指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +リスナーに指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ユーザーがチャンネルの横でアシスタントスレッドを開いた場合、そのチャンネルの情報は、そのスレッドの `AssistantThreadContext` データとして保持され、 `get_thread_context` ユーティリティを使ってアクセスすることができます。Bolt がこのユーティリティを提供している理由は、後続のユーザーメッセージ投稿のイベントペイロードに最新のスレッドのコンテキスト情報は含まれないためです。そのため、アプリはコンテキスト情報が変更されたタイミングでそれを何らかの方法で保存し、後続のメッセージイベントのリスナーコードから参照できるようにする必要があります。 diff --git a/docs/japanese/concepts/async.md b/docs/japanese/concepts/async.md index cc38886d4..6687dcff5 100644 --- a/docs/japanese/concepts/async.md +++ b/docs/japanese/concepts/async.md @@ -1,8 +1,8 @@ # Async(asyncio)の使用 -非同期バージョンの Bolt を使用する場合は、`App` の代わりに `AsyncApp` インスタンスをインポートして初期化します。`AsyncApp` では AIOHTTP を使って API リクエストを行うため、`aiohttp` をインストールする必要があります(`requirements.txt` に追記するか、`pip install aiohttp` を実行します)。 +非同期バージョンの Bolt を使用する場合は、`App` の代わりに `AsyncApp` インスタンスをインポートして初期化します。`AsyncApp` では [AIOHTTP](https://docs.aiohttp.org/) を使って API リクエストを行うため、`aiohttp` をインストールする必要があります(`requirements.txt` に追記するか、`pip install aiohttp` を実行します)。 -非同期バージョンのプロジェクトのサンプルは、リポジトリの `examples` フォルダにあります。 +非同期バージョンのプロジェクトのサンプルは、リポジトリの [`examples` フォルダ](https://github.com/slackapi/bolt-python/tree/main/examples)にあります。 ```python # aiohttp のインストールが必要です diff --git a/docs/japanese/concepts/commands.md b/docs/japanese/concepts/commands.md index c89568dbe..ebb43c4d3 100644 --- a/docs/japanese/concepts/commands.md +++ b/docs/japanese/concepts/commands.md @@ -8,7 +8,7 @@ アプリの設定でコマンドを登録するときは、リクエスト URL の末尾に `/slack/events` をつけます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # echoコマンドは受け取ったコマンドをそのまま返す @app.command("/echo") diff --git a/docs/japanese/concepts/event-listening.md b/docs/japanese/concepts/event-listening.md index c13638226..7b21f1fa4 100644 --- a/docs/japanese/concepts/event-listening.md +++ b/docs/japanese/concepts/event-listening.md @@ -4,7 +4,7 @@ `event()` メソッドには `str` 型の `eventType` を指定する必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # ユーザーがワークスペースに参加した際に、自己紹介を促すメッセージを指定のチャンネルに送信 @app.event("team_join") diff --git a/docs/japanese/concepts/global-middleware.md b/docs/japanese/concepts/global-middleware.md index 884008090..01ca417ac 100644 --- a/docs/japanese/concepts/global-middleware.md +++ b/docs/japanese/concepts/global-middleware.md @@ -4,7 +4,7 @@ グローバルミドルウェアでもリスナーミドルウェアでも、次のミドルウェアに実行チェーンの制御をリレーするために、`next()` を呼び出す必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python @app.use diff --git a/docs/japanese/concepts/listener-middleware.md b/docs/japanese/concepts/listener-middleware.md index 2b3ea9323..425ae4ea7 100644 --- a/docs/japanese/concepts/listener-middleware.md +++ b/docs/japanese/concepts/listener-middleware.md @@ -4,7 +4,7 @@ 非常にシンプルなリスナーミドルウェアの場合であれば、`next()` メソッドを呼び出す代わりに `bool` 値(処理を継続したい場合は `True`)を返すだけで済む「リスナーマッチャー」を使うとよいでしょう。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # ボットからのメッセージをフィルタリングするリスナーミドルウェア diff --git a/docs/japanese/concepts/message-listening.md b/docs/japanese/concepts/message-listening.md index 824ac67c8..dae729b51 100644 --- a/docs/japanese/concepts/message-listening.md +++ b/docs/japanese/concepts/message-listening.md @@ -4,7 +4,7 @@ `message()` の引数には `str` 型または `re.Pattern` オブジェクトを指定できます。この条件のパターンに一致しないメッセージは除外されます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # '👋' が含まれるすべてのメッセージに一致 @app.message(":wave:") diff --git a/docs/japanese/concepts/message-sending.md b/docs/japanese/concepts/message-sending.md index a299144b6..ace67051b 100644 --- a/docs/japanese/concepts/message-sending.md +++ b/docs/japanese/concepts/message-sending.md @@ -4,7 +4,7 @@ リスナー関数の外でメッセージを送信したい場合や、より高度な処理(特定のエラーの処理など)を実行したい場合は、[Bolt インスタンスにアタッチされたクライアント](/tools/bolt-python/concepts/web-api)の `client.chat_postMessage` を呼び出します。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # 'knock knock' が含まれるメッセージをリッスンし、イタリック体で 'Who's there?' と返信 @app.message("knock knock") diff --git a/docs/japanese/concepts/opening-modals.md b/docs/japanese/concepts/opening-modals.md index 68e3b947c..65342afb1 100644 --- a/docs/japanese/concepts/opening-modals.md +++ b/docs/japanese/concepts/opening-modals.md @@ -1,12 +1,12 @@ # モーダルの開始 -モーダルは、ユーザーからのデータの入力を受け付けたり、動的な情報を表示したりするためのインターフェイスです。組み込みの APIクライアントの `views.open` メソッドに、有効な `trigger_id` とビューのペイロードを指定してモーダルを開始します。 +[モーダル](/surfaces/modals)は、ユーザーからのデータの入力を受け付けたり、動的な情報を表示したりするためのインターフェイスです。組み込みの APIクライアントの [`views.open`](/reference/methods/views.open/) メソッドに、有効な `trigger_id` と[ビューのペイロード](/reference/interaction-payloads/view-interactions-payload/#view_submission)を指定してモーダルを開始します。 ショートカットの実行、ボタンを押下、選択メニューの操作などの操作の場合、Request URL に送信されるペイロードには `trigger_id` が含まれます。 -モーダルの生成方法についての詳細は、API ドキュメントを参照してください。 +モーダルの生成方法についての詳細は、[API ドキュメント](/surfaces/modals#composing_views)を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # ショートカットの呼び出しをリッスン diff --git a/docs/japanese/concepts/select-menu-options.md b/docs/japanese/concepts/select-menu-options.md index 2c12af623..4f3a5f357 100644 --- a/docs/japanese/concepts/select-menu-options.md +++ b/docs/japanese/concepts/select-menu-options.md @@ -10,7 +10,7 @@ さらに、ユーザーが入力したキーワードに基づいたオプションを返すようフィルタリングロジックを適用することもできます。 これは `payload` という引数の ` value` の値に基づいて、それぞれのパターンで異なるオプションの一覧を返すように実装することができます。 Bolt for Python のすべてのリスナーやミドルウェアでは、[多くの有用な引数](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)にアクセスすることができますので、チェックしてみてください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # 外部データを使用する選択メニューオプションに応答するサンプル例 @app.options("external_action") diff --git a/docs/japanese/concepts/shortcuts.md b/docs/japanese/concepts/shortcuts.md index d9a8ba050..39fb10ba8 100644 --- a/docs/japanese/concepts/shortcuts.md +++ b/docs/japanese/concepts/shortcuts.md @@ -12,7 +12,7 @@ ⚠️ グローバルショートカットのペイロードにはチャンネル ID が **含まれません**。アプリでチャンネル ID を取得する必要がある場合は、モーダル内に [`conversations_select`](/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) エレメントを配置します。メッセージショートカットにはチャンネル ID が含まれます。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # 'open_modal' という callback_id のショートカットをリッスン @app.shortcut("open_modal") diff --git a/docs/japanese/concepts/updating-pushing-views.md b/docs/japanese/concepts/updating-pushing-views.md index cc32f5b69..2bbaf5ae5 100644 --- a/docs/japanese/concepts/updating-pushing-views.md +++ b/docs/japanese/concepts/updating-pushing-views.md @@ -1,6 +1,6 @@ # モーダルの更新と多重表示 -モーダル内では、複数のモーダルをスタックのように重ねることができます。`views_open` という APIを呼び出すと、親となるとなるモーダルビューが追加されます。この最初の呼び出しの後、`views_update` を呼び出すことでそのビューを更新することができます。また、`views_push` を呼び出すと、親のモーダルの上にさらに新しいモーダルビューを重ねることもできます。 +モーダル内では、複数のモーダルをスタックのように重ねることができます。[`views_open`](/reference/methods/views.open/) という APIを呼び出すと、親となるとなるモーダルビューが追加されます。この最初の呼び出しの後、[`views_update`](/reference/methods/views.update/) を呼び出すことでそのビューを更新することができます。また、[`views_push`](/reference/methods/views.push) を呼び出すと、親のモーダルの上にさらに新しいモーダルビューを重ねることもできます。 **`views_update`** @@ -8,11 +8,11 @@ **`views_push`** -既存のモーダルの上に新しいモーダルをスタックのように追加する場合は、組み込みのクライアントで `views_push` API を呼び出します。この API 呼び出しでは、有効な `trigger_id` と新しいビューのペイロードを指定します。`views_push` の引数は モーダルの開始 と同じです。モーダルを開いた後、このモーダルのスタックに追加できるモーダルビューは 2 つまでです。 +既存のモーダルの上に新しいモーダルをスタックのように追加する場合は、組み込みのクライアントで `views_push` API を呼び出します。この API 呼び出しでは、有効な `trigger_id` と新しい[ビューのペイロード](/reference/interaction-payloads/view-interactions-payload/#view_submission)を指定します。`views_push` の引数は [モーダルの開始](#creating-modals) と同じです。モーダルを開いた後、このモーダルのスタックに追加できるモーダルビューは 2 つまでです。 -モーダルの更新と多重表示に関する詳細は、API ドキュメントを参照してください。 +モーダルの更新と多重表示に関する詳細は、[API ドキュメント](/surfaces/modals)を参照してください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # モーダルに含まれる、`button_abc` という action_id のボタンの呼び出しをリッスン diff --git a/docs/japanese/concepts/view-submissions.md b/docs/japanese/concepts/view-submissions.md index 5ae78f173..f82922004 100644 --- a/docs/japanese/concepts/view-submissions.md +++ b/docs/japanese/concepts/view-submissions.md @@ -1,6 +1,6 @@ # モーダルの送信のリスニング -モーダルのペイロードに `input` ブロックを含める場合、その入力値を受け取るために`view_submission` リクエストをリッスンする必要があります。`view_submission` リクエストのリッスンには、組み込みの`view()` メソッドを利用することができます。`view()` の引数には、`str` 型または `re.Pattern` 型の `callback_id` を指定します。 +[モーダルのペイロード](/reference/interaction-payloads/view-interactions-payload/#view_submission)に `input` ブロックを含める場合、その入力値を受け取るために`view_submission` リクエストをリッスンする必要があります。`view_submission` リクエストのリッスンには、組み込みの`view()` メソッドを利用することができます。`view()` の引数には、`str` 型または `re.Pattern` 型の `callback_id` を指定します。 `input` ブロックの値にアクセスするには `state` オブジェクトを参照します。`state` 内には `values` というオブジェクトがあり、`block_id` と一意の `action_id` に紐づける形で入力値を保持しています。 @@ -19,9 +19,9 @@ def handle_submission(ack, body): # https://app.slack.com/block-kit-builder/#%7B%22type%22:%22modal%22,%22callback_id%22:%22view_1%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22My%20App%22,%22emoji%22:true%7D,%22blocks%22:%5B%5D%7D ack(response_action="update", view=build_new_view(body)) ``` -この例と同様に、モーダルでの送信リクエストに対して、エラーを表示するためのオプションもあります。 +この例と同様に、モーダルでの送信リクエストに対して、[エラーを表示する](/surfaces/modals#displaying_errors)ためのオプションもあります。 -モーダルの送信について詳しくは、API ドキュメントを参照してください。 +モーダルの送信について詳しくは、[API ドキュメント](/surfaces/modals#interactions)を参照してください。 --- @@ -29,7 +29,7 @@ def handle_submission(ack, body): `view_closed` リクエストをリッスンするためには `callback_id` を指定して、かつ `notify_on_close` 属性をモーダルのビューに設定する必要があります。以下のコード例をご覧ください。 -よく詳しい情報は、API ドキュメントを参照してください。 +よく詳しい情報は、[API ドキュメント](/surfaces/modals#interactions)を参照してください。 ```python client.views_open( @@ -56,7 +56,7 @@ def handle_view_closed(ack, body, logger): logger.info(body) ``` -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python # view_submission リクエストを処理 diff --git a/docs/japanese/concepts/web-api.md b/docs/japanese/concepts/web-api.md index 7a674b9b2..abb8e4121 100644 --- a/docs/japanese/concepts/web-api.md +++ b/docs/japanese/concepts/web-api.md @@ -4,7 +4,7 @@ Bolt の初期化に使用するトークンは `context` オブジェクトに設定されます。このトークンは、多くの Web API メソッドを呼び出す際に必要となります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 +指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 ```python @app.message("wake me up") def say_hello(client, message): diff --git a/docs/japanese/getting-started.md b/docs/japanese/getting-started.md index 30538bf94..41e6ae5cd 100644 --- a/docs/japanese/getting-started.md +++ b/docs/japanese/getting-started.md @@ -48,7 +48,7 @@ Slack アプリで使用できるトークンには、ユーザートークン 6. 左サイドメニューの「**Socket Mode**」を有効にします。 -:::tip[トークンはパスワードと同様に取り扱い、[安全な方法で保管してください](/authentication/best-practices-for-security)。アプリはこのトークンを使って Slack ワークスペースで投稿をしたり、情報の取得をしたりします。] +:::tip[トークンはパスワードと同様に取り扱い、[安全な方法で保管してください](/security)。アプリはこのトークンを使って Slack ワークスペースで投稿をしたり、情報の取得をしたりします。] ::: @@ -91,7 +91,7 @@ export SLACK_APP_TOKEN=<アプリレベルトークン> ``` :::warning[🔒 全てのトークンは安全に保管してください。] -少なくともパブリックなバージョン管理にチェックインするようなことは避けるべきでしょう。また、上にあった例のように環境変数を介してアクセスするようにしてください。詳細な情報は [アプリのセキュリティのベストプラクティス](/authentication/best-practices-for-security)のドキュメントを参照してください。 +少なくともパブリックなバージョン管理にチェックインするようなことは避けるべきでしょう。また、上にあった例のように環境変数を介してアクセスするようにしてください。詳細な情報は [アプリのセキュリティのベストプラクティス](/security)のドキュメントを参照してください。 ::: diff --git a/docs/japanese/legacy/steps-from-apps.md b/docs/japanese/legacy/steps-from-apps.md index a7ef2e04a..802802ab3 100644 --- a/docs/japanese/legacy/steps-from-apps.md +++ b/docs/japanese/legacy/steps-from-apps.md @@ -10,8 +10,6 @@ ワークフローステップを機能させるためには、これら 3 つのイベントすべてに対応する必要があります。 -アプリを使ったワークフローステップに関する詳細は、[API ドキュメント](/legacy/legacy-steps-from-apps/)を参照してください。 - ## ステップの定義 ワークフローステップの作成には、Bolt が提供する `WorkflowStep` クラスを利用します。 @@ -24,7 +22,7 @@ また、デコレーターとして利用できる `WorkflowStepBuilder` クラスを使ってワークフローステップを定義することもできます。 詳細は、[こちらのドキュメント](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder)のコード例などを参考にしてください。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください([共通](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [ステップ用](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) ```python import os @@ -59,15 +57,13 @@ app.step(ws) ## ステップの追加・編集 -作成したワークフローステップがワークフローに追加またはその設定を変更されるタイミングで、[`workflow_step_edit` イベントがアプリに送信されます](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step_edit-payload)。このイベントがアプリに届くと、`WorkflowStep` で設定した `edit` コールバックが実行されます。 +作成したワークフローステップがワークフローに追加またはその設定を変更されるタイミングで、`workflow_step_edit` イベントがアプリに送信されます。このイベントがアプリに届くと、`WorkflowStep` で設定した `edit` コールバックが実行されます。 -ステップの追加と編集のどちらが行われるときも、[ワークフローステップの設定モーダル](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object)をビルダーに送信する必要があります。このモーダルは、そのステップ独自の設定を選択するための場所です。通常のモーダルより制限が強く、例えば `title`、`submit`、`close` のプロパティを含めることができません。設定モーダルの `callback_id` は、デフォルトではワークフローステップと同じものになります。 +ステップの追加と編集のどちらが行われるときも、ワークフローステップの設定モーダルをビルダーに送信する必要があります。このモーダルは、そのステップ独自の設定を選択するための場所です。通常のモーダルより制限が強く、例えば `title`、`submit`、`close` のプロパティを含めることができません。設定モーダルの `callback_id` は、デフォルトではワークフローステップと同じものになります。 `edit` コールバック内で `configure()` ユーティリティを使用すると、対応する `blocks` 引数にビューのblocks 部分だけを渡して、ステップの設定モーダルを簡単に表示させることができます。必要な入力内容が揃うまで設定の保存を無効にするには、`True` の値をセットした `submit_disabled` を渡します。 -設定モーダルの開き方に関する詳細は、[こちらのドキュメント](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-configuration-view-object)を参照してください。 - -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください([共通](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [ステップ用](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) ```python def edit(ack, step, configure): @@ -117,9 +113,7 @@ app.step(ws) - `step_name` : ステップのデフォルトの名前をオーバーライドします。 - `step_image_url` : ステップのデフォルトの画像をオーバーライドします。 -これらのパラメータの構成方法に関する詳細は、[こちらのドキュメント](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object)を参照してください。 - -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください([共通](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [ステップ用](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) ```python def save(ack, view, update): @@ -158,13 +152,14 @@ app.step(ws) ## ステップの実行 -エンドユーザーがワークフローステップを実行すると、アプリに [`workflow_step_execute` イベントが送信されます](/legacy/legacy-steps-from-apps/legacy-steps-from-apps-workflow_step-object)。このイベントがアプリに届くと、`WorkflowStep` で設定した `execute` コールバックが実行されます。 +エンドユーザーがワークフローステップを実行すると、アプリに `workflow_step_execute` イベントが送信されます。このイベントがアプリに届くと、`WorkflowStep` で設定した `execute` コールバックが実行されます。 `save` コールバックで取り出した `inputs` を使って、サードパーティの API を呼び出す、情報をデータベースに保存する、ユーザーのホームタブを更新するといった処理を実行することができます。また、ワークフローの後続のステップで利用する出力値を `outputs` オブジェクトに設定します。 `execute` コールバック内では、`complete()` を呼び出してステップの実行が成功したことを示すか、`fail()` を呼び出してステップの実行が失敗したことを示す必要があります。 -指定可能な引数の一覧はモジュールドキュメントを参考にしてください(共通 / ステップ用 +指定可能な引数の一覧はモジュールドキュメントを参考にしてください([共通](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [ステップ用](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) + ```python def execute(step, complete, fail): inputs = step["inputs"] From 64e09c5e303af06b1c5643b14b459b9794f21756 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 08:41:42 -0800 Subject: [PATCH 173/282] chore(deps): bump actions/checkout from 6.0.0 to 6.0.1 (#1420) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codecov.yml | 2 +- .github/workflows/flake8.yml | 2 +- .github/workflows/mypy.yml | 2 +- .github/workflows/pypi-release.yml | 2 +- .github/workflows/tests.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index f93864817..3340efe8c 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -18,7 +18,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index b0602d60a..8305fe645 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 650c8f9bd..353bad38b 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 80fac6d8c..9afa946aa 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.release.tag_name || github.ref }} persist-credentials: false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6de00d7da..0f4f0c4b9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,7 +28,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} From 7d95b8ebeffa3e9ebee8890302aba7f0d5e68520 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:48:16 +0000 Subject: [PATCH 174/282] chore(deps): bump actions/stale from 10.1.0 to 10.1.1 (#1419) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- .github/workflows/triage-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index 85ccb72aa..cf13d3afc 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -16,7 +16,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 + - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 with: days-before-issue-stale: 30 days-before-issue-close: 10 From a1aa7139c4126ab3fbc23eba5f8f900ae73ecffd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:54:36 +0000 Subject: [PATCH 175/282] chore(deps): bump mypy from 1.19.0 to 1.19.1 (#1418) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index f9fbcdac1..7609eb52e 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ -mypy==1.19.0 +mypy==1.19.1 flake8==7.3.0 black==25.1.0 From 252bf23b05cf82a515aa6dbad28b0cd0d5000f4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:46:03 +0000 Subject: [PATCH 176/282] chore(deps): bump codecov/codecov-action from 5.5.1 to 5.5.2 (#1417) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codecov.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 3340efe8c..4485c27ae 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -36,7 +36,7 @@ jobs: run: | pytest --cov=./slack_bolt/ --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: fail_ci_if_error: true report_type: coverage diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0f4f0c4b9..3ba2a17f2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,7 +77,7 @@ jobs: pytest tests/scenario_tests_async/ --junitxml=reports/test_scenario_async.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: directory: ./reports/ fail_ci_if_error: true From 13e4a8e44b50b9a80072f4bb232136c9bc8fd7cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:50:08 +0000 Subject: [PATCH 177/282] chore(deps): bump actions/download-artifact from 6.0.0 to 7.0.0 (#1416) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 9afa946aa..7f97da9ed 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -52,7 +52,7 @@ jobs: steps: - name: Retrieve dist folder - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: release-dist path: dist/ @@ -76,7 +76,7 @@ jobs: steps: - name: Retrieve dist folder - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: release-dist path: dist/ From c8511da19b6d7bcd4e733ac1e50bf244e02247ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:54:30 +0000 Subject: [PATCH 178/282] chore(deps): bump actions/upload-artifact from 5.0.0 to 6.0.0 (#1415) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 7f97da9ed..89a18c827 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -33,7 +33,7 @@ jobs: scripts/build_pypi_package.sh - name: Persist dist folder - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: release-dist path: dist/ From 69181155b266e88331f9e0d17844a2ccf37265be Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 15 Jan 2026 06:57:13 -0800 Subject: [PATCH 179/282] chore: update the ci pipeline to match other patterns (#1422) --- .github/workflows/{tests.yml => ci-build.yml} | 82 ++++++++++++++++++- .github/workflows/codecov.yml | 44 ---------- .github/workflows/flake8.yml | 28 ------- .github/workflows/mypy.yml | 28 ------- scripts/format.sh | 7 +- scripts/install_all_and_run_tests.sh | 27 +++--- scripts/lint.sh | 12 +++ scripts/run_flake8.sh | 7 -- scripts/run_mypy.sh | 15 ++-- scripts/run_tests.sh | 8 +- 10 files changed, 121 insertions(+), 137 deletions(-) rename .github/workflows/{tests.yml => ci-build.yml} (60%) delete mode 100644 .github/workflows/codecov.yml delete mode 100644 .github/workflows/flake8.yml delete mode 100644 .github/workflows/mypy.yml create mode 100755 scripts/lint.sh delete mode 100755 scripts/run_flake8.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/ci-build.yml similarity index 60% rename from .github/workflows/tests.yml rename to .github/workflows/ci-build.yml index 3ba2a17f2..a11fa10d7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/ci-build.yml @@ -1,4 +1,4 @@ -name: Run all the unit tests +name: Python CI on: push: @@ -9,8 +9,46 @@ on: - cron: "0 0 * * *" workflow_dispatch: +env: + LATEST_SUPPORTED_PY: "3.14" + jobs: - build: + lint: + name: Lint + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: ${{ env.LATEST_SUPPORTED_PY }} + - name: Run lint verification + run: ./scripts/lint.sh + + typecheck: + name: Typecheck + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: ${{ env.LATEST_SUPPORTED_PY }} + - name: Run mypy verification + run: ./scripts/run_mypy.sh + + unittest: + name: Unit tests runs-on: ubuntu-22.04 timeout-minutes: 10 strategy: @@ -85,10 +123,48 @@ jobs: report_type: test_results token: ${{ secrets.CODECOV_TOKEN }} verbose: true + + codecov: + name: Code Coverage + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + env: + BOLT_PYTHON_CODECOV_RUNNING: "1" + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: ${{ env.LATEST_SUPPORTED_PY }} + - name: Install dependencies + run: | + pip install -U pip + pip install . + pip install -r requirements/adapter.txt + pip install -r requirements/testing.txt + pip install -r requirements/adapter_testing.txt + - name: Run all tests for codecov + run: | + pytest --cov=./slack_bolt/ --cov-report=xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + with: + fail_ci_if_error: true + report_type: coverage + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + notifications: name: Regression notifications runs-on: ubuntu-latest - needs: build + needs: + - lint + - typecheck + - unittest if: ${{ !success() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' }} steps: - name: Send notifications of failing tests diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml deleted file mode 100644 index 4485c27ae..000000000 --- a/.github/workflows/codecov.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Run codecov - -on: - push: - branches: - - main - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - python-version: ["3.14"] - permissions: - contents: read - env: - BOLT_PYTHON_CODECOV_RUNNING: "1" - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip install -U pip - pip install . - pip install -r requirements/adapter.txt - pip install -r requirements/testing.txt - pip install -r requirements/adapter_testing.txt - - name: Run all tests for codecov - run: | - pytest --cov=./slack_bolt/ --cov-report=xml - - name: Upload coverage to Codecov - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 - with: - fail_ci_if_error: true - report_type: coverage - token: ${{ secrets.CODECOV_TOKEN }} - verbose: true diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml deleted file mode 100644 index 8305fe645..000000000 --- a/.github/workflows/flake8.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Run flake8 validation - -on: - push: - branches: - - main - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 20 - strategy: - matrix: - python-version: ["3.14"] - permissions: - contents: read - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 - with: - python-version: ${{ matrix.python-version }} - - name: Run flake8 verification - run: | - ./scripts/run_flake8.sh diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml deleted file mode 100644 index 353bad38b..000000000 --- a/.github/workflows/mypy.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Run mypy validation - -on: - push: - branches: - - main - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 20 - strategy: - matrix: - python-version: ["3.14"] - permissions: - contents: read - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - persist-credentials: false - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 - with: - python-version: ${{ matrix.python-version }} - - name: Run mypy verification - run: | - ./scripts/run_mypy.sh diff --git a/scripts/format.sh b/scripts/format.sh index 77cecf9e4..e73bcdac4 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -4,7 +4,10 @@ script_dir=`dirname $0` cd ${script_dir}/.. -pip install -U pip -pip install -U -r requirements/tools.txt +if [[ "$1" != "--no-install" ]]; then + export PIP_REQUIRE_VIRTUALENV=1 + pip install -U pip + pip install -U -r requirements/tools.txt +fi black slack_bolt/ tests/ diff --git a/scripts/install_all_and_run_tests.sh b/scripts/install_all_and_run_tests.sh index 1f2690414..2bb9a2050 100755 --- a/scripts/install_all_and_run_tests.sh +++ b/scripts/install_all_and_run_tests.sh @@ -16,25 +16,20 @@ pip uninstall python-lambda test_target="$1" pip install -U -e . +pip install -U -r requirements/testing.txt +pip install -U -r requirements/adapter.txt +pip install -U -r requirements/adapter_testing.txt +pip install -U -r requirements/tools.txt +# To avoid errors due to the old versions of click forced by Chalice +pip install -U pip click if [[ $test_target != "" ]] then - pip install -U -r requirements/testing.txt && \ - pip install -U -r requirements/adapter.txt && \ - pip install -U -r requirements/adapter_testing.txt && \ - # To avoid errors due to the old versions of click forced by Chalice - pip install -U pip click && \ - black slack_bolt/ tests/ && \ + ./scripts/format.sh --no-install pytest $1 else - pip install -U -r requirements/testing.txt && \ - pip install -U -r requirements/adapter.txt && \ - pip install -U -r requirements/adapter_testing.txt && \ - pip install -r requirements/tools.txt && \ - # To avoid errors due to the old versions of click forced by Chalice - pip install -U pip click && \ - black slack_bolt/ tests/ && \ - flake8 slack_bolt/ && flake8 examples/ - pytest && \ - mypy --config-file pyproject.toml + ./scripts/format.sh --no-install + ./scripts/lint.sh --no-install + pytest + ./scripts/run_mypy.sh --no-install fi diff --git a/scripts/lint.sh b/scripts/lint.sh new file mode 100755 index 000000000..efee01ebc --- /dev/null +++ b/scripts/lint.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# ./scripts/lint.sh + +script_dir=$(dirname $0) +cd ${script_dir}/.. + +if [[ "$1" != "--no-install" ]]; then + pip install -U pip + pip install -U -r requirements/tools.txt +fi + +flake8 slack_bolt/ && flake8 examples/ diff --git a/scripts/run_flake8.sh b/scripts/run_flake8.sh deleted file mode 100755 index e523920f9..000000000 --- a/scripts/run_flake8.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# ./scripts/run_flake8.sh - -script_dir=$(dirname $0) -cd ${script_dir}/.. && \ - pip install -U -r requirements/tools.txt && \ - flake8 slack_bolt/ && flake8 examples/ diff --git a/scripts/run_mypy.sh b/scripts/run_mypy.sh index c018443b7..27589b348 100755 --- a/scripts/run_mypy.sh +++ b/scripts/run_mypy.sh @@ -2,9 +2,14 @@ # ./scripts/run_mypy.sh script_dir=$(dirname $0) -cd ${script_dir}/.. && \ +cd ${script_dir}/.. + +if [[ "$1" != "--no-install" ]]; then + pip install -U pip pip install -U . - pip install -U -r requirements/async.txt && \ - pip install -U -r requirements/adapter.txt && \ - pip install -U -r requirements/tools.txt && \ - mypy --config-file pyproject.toml + pip install -U -r requirements/async.txt + pip install -U -r requirements/adapter.txt + pip install -U -r requirements/tools.txt +fi + +mypy --config-file pyproject.toml diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index e4cc99709..cdac3c71c 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -7,12 +7,12 @@ script_dir=`dirname $0` cd ${script_dir}/.. test_target="$1" -python_version=`python --version | awk '{print $2}'` + +./scripts/format.sh --no-install if [[ $test_target != "" ]] then - black slack_bolt/ tests/ && \ - pytest -vv $1 + pytest -vv $1 else - black slack_bolt/ tests/ && pytest + pytest fi From c3929dfd41adb47f303cb7e27120fdb8a2690e45 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Tue, 3 Feb 2026 18:42:59 -0800 Subject: [PATCH 180/282] ci(deps): auto-approve / auto-merge dependencies from dependabot (#1434) --- .github/workflows/dependencies.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/dependencies.yml diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml new file mode 100644 index 000000000..824d57701 --- /dev/null +++ b/.github/workflows/dependencies.yml @@ -0,0 +1,29 @@ +name: Merge updates to dependencies +on: + pull_request: +jobs: + dependabot: + name: "@dependabot" + if: github.event.pull_request.user.login == 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Collect metadata + id: metadata + uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Approve + if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' + run: gh pr review --approve "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + - name: Automerge + if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' + run: gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From fbbaaa08f7d7c024302c1b916db3fc3742eaa35f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:53:59 +0000 Subject: [PATCH 181/282] chore(deps): bump actions/checkout from 6.0.1 to 6.0.2 (#1424) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Brooks --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index a11fa10d7..7c360b98d 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -20,7 +20,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -37,7 +37,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -66,7 +66,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} @@ -133,7 +133,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 89a18c827..a072d20b0 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.release.tag_name || github.ref }} persist-credentials: false From e5cf0f780760c443a7a5802a2d2c19fb4072c9aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 17:02:23 +0000 Subject: [PATCH 182/282] chore(deps): bump actions/setup-python from 6.1.0 to 6.2.0 (#1425) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 7c360b98d..6555a6531 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -24,7 +24,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Run lint verification @@ -41,7 +41,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Run mypy verification @@ -70,7 +70,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} - name: Install synchronous dependencies @@ -137,7 +137,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Install dependencies diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index a072d20b0..9c9003c92 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Set up Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.x" From 868cedbce8164a5de5fdbe7db930f28ec6ac85c8 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 10 Feb 2026 16:43:35 -0800 Subject: [PATCH 183/282] fix: pin setuptools to maintain support for pyramid adapter (#1436) --- .github/dependabot.yml | 4 ++++ requirements/adapter.txt | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index dc523d227..774d13833 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,10 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 5 + ignore: + # setuptools is pinned due to pyramid's dependency on deprecated pkg_resources + # See: https://github.com/Pylons/pyramid/issues/3731 + - dependency-name: "setuptools" - package-ecosystem: "github-actions" directory: "/" schedule: diff --git a/requirements/adapter.txt b/requirements/adapter.txt index b2097bcdb..c19c7713b 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -13,6 +13,7 @@ fastapi>=0.70.0,<1 Flask>=1,<4 Werkzeug>=2,<4 pyramid>=1,<3 +setuptools<82 # Pinned: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 # Sanic and its dependencies # Note: Sanic imports tracerite with wild card versions From 1ad642efded7629ac9b87988a84906783e765560 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Sun, 15 Feb 2026 22:23:17 -0800 Subject: [PATCH 184/282] Add 'agent: BoltAgent' listener argument (#1437) Co-authored-by: Luke Russell Co-authored-by: William Bergamin --- .gitignore | 3 + docs/english/_sidebar.json | 5 + docs/english/experiments.md | 34 ++++ slack_bolt/__init__.py | 2 + slack_bolt/adapter/__init__.py | 3 +- slack_bolt/agent/__init__.py | 5 + slack_bolt/agent/agent.py | 73 ++++++++ slack_bolt/agent/async_agent.py | 70 ++++++++ slack_bolt/kwargs_injection/args.py | 5 + slack_bolt/kwargs_injection/async_args.py | 5 + slack_bolt/kwargs_injection/async_utils.py | 23 ++- slack_bolt/kwargs_injection/utils.py | 23 ++- slack_bolt/warning/__init__.py | 6 + tests/scenario_tests/test_events_agent.py | 162 +++++++++++++++++ .../scenario_tests_async/test_events_agent.py | 169 ++++++++++++++++++ tests/slack_bolt/agent/__init__.py | 0 tests/slack_bolt/agent/test_agent.py | 103 +++++++++++ tests/slack_bolt_async/agent/__init__.py | 0 .../agent/test_async_agent.py | 114 ++++++++++++ 19 files changed, 799 insertions(+), 6 deletions(-) create mode 100644 docs/english/experiments.md create mode 100644 slack_bolt/agent/__init__.py create mode 100644 slack_bolt/agent/agent.py create mode 100644 slack_bolt/agent/async_agent.py create mode 100644 slack_bolt/warning/__init__.py create mode 100644 tests/scenario_tests/test_events_agent.py create mode 100644 tests/scenario_tests_async/test_events_agent.py create mode 100644 tests/slack_bolt/agent/__init__.py create mode 100644 tests/slack_bolt/agent/test_agent.py create mode 100644 tests/slack_bolt_async/agent/__init__.py create mode 100644 tests/slack_bolt_async/agent/test_async_agent.py diff --git a/.gitignore b/.gitignore index 2549060e7..b28dfa9ed 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ venv/ .venv* .env/ +# claude +.claude/*.local.json + # codecov / coverage .coverage cov_* diff --git a/docs/english/_sidebar.json b/docs/english/_sidebar.json index 859c4b52f..eab9d94f8 100644 --- a/docs/english/_sidebar.json +++ b/docs/english/_sidebar.json @@ -85,6 +85,11 @@ "tools/bolt-python/concepts/token-rotation" ] }, + { + "type": "category", + "label": "Experiments", + "items": ["tools/bolt-python/experiments"] + }, { "type": "category", "label": "Legacy", diff --git a/docs/english/experiments.md b/docs/english/experiments.md new file mode 100644 index 000000000..681c8cbc6 --- /dev/null +++ b/docs/english/experiments.md @@ -0,0 +1,34 @@ +# Experiments + +Bolt for Python includes experimental features still under active development. These features may be fleeting, may not be perfectly polished, and should be thought of as available for use "at your own risk." + +Experimental features are categorized as `semver:patch` until the experimental status is removed. + +We love feedback from our community, so we encourage you to explore and interact with the [GitHub repo](https://github.com/slackapi/bolt-python). Contributions, bug reports, and any feedback are all helpful; let us nurture the Slack CLI together to help make building Slack apps more pleasant for everyone. + +## Available experiments +* [Agent listener argument](#agent) + +## Agent listener argument {#agent} + +The `agent: BoltAgent` listener argument provides access to AI agent-related features. + +The `BoltAgent` and `AsyncBoltAgent` classes offer a `chat_stream()` method that comes pre-configured with event context defaults: `channel_id`, `thread_ts`, `team_id`, and `user_id` fields. + +The listener argument is wired into the Bolt `kwargs` injection system, so listeners can declare it as a parameter or access it via the `context.agent` property. + +### Example + +```python +from slack_bolt import BoltAgent + +@app.event("app_mention") +def handle_mention(agent: BoltAgent): + stream = agent.chat_stream() + stream.append(markdown_text="Hello!") + stream.stop() +``` + +### Limitations + +The `chat_stream()` method currently only works when the `thread_ts` field is available in the event context (DMs and threaded replies). Top-level channel messages do not have a `thread_ts` field, and the `ts` field is not yet provided to `BoltAgent`. \ No newline at end of file diff --git a/slack_bolt/__init__.py b/slack_bolt/__init__.py index 6331925f8..4e43252fd 100644 --- a/slack_bolt/__init__.py +++ b/slack_bolt/__init__.py @@ -21,6 +21,7 @@ from .response import BoltResponse # AI Agents & Assistants +from .agent import BoltAgent from .middleware.assistant.assistant import ( Assistant, ) @@ -46,6 +47,7 @@ "CustomListenerMatcher", "BoltRequest", "BoltResponse", + "BoltAgent", "Assistant", "AssistantThreadContext", "AssistantThreadContextStore", diff --git a/slack_bolt/adapter/__init__.py b/slack_bolt/adapter/__init__.py index f339226bc..9ca556e52 100644 --- a/slack_bolt/adapter/__init__.py +++ b/slack_bolt/adapter/__init__.py @@ -1,2 +1 @@ -"""Adapter modules for running Bolt apps along with Web frameworks or Socket Mode. -""" +"""Adapter modules for running Bolt apps along with Web frameworks or Socket Mode.""" diff --git a/slack_bolt/agent/__init__.py b/slack_bolt/agent/__init__.py new file mode 100644 index 000000000..4d751f27f --- /dev/null +++ b/slack_bolt/agent/__init__.py @@ -0,0 +1,5 @@ +from .agent import BoltAgent + +__all__ = [ + "BoltAgent", +] diff --git a/slack_bolt/agent/agent.py b/slack_bolt/agent/agent.py new file mode 100644 index 000000000..db1c78aa9 --- /dev/null +++ b/slack_bolt/agent/agent.py @@ -0,0 +1,73 @@ +from typing import Optional + +from slack_sdk import WebClient +from slack_sdk.web.chat_stream import ChatStream + + +class BoltAgent: + """Agent listener argument for building AI-powered Slack agents. + + Experimental: + This API is experimental and may change in future releases. + + FIXME: chat_stream() only works when thread_ts is available (DMs and threaded replies). + It does not work on channel messages because ts is not provided to BoltAgent yet. + + @app.event("app_mention") + def handle_mention(agent): + stream = agent.chat_stream() + stream.append(markdown_text="Hello!") + stream.stop() + """ + + def __init__( + self, + *, + client: WebClient, + channel_id: Optional[str] = None, + thread_ts: Optional[str] = None, + team_id: Optional[str] = None, + user_id: Optional[str] = None, + ): + self._client = client + self._channel_id = channel_id + self._thread_ts = thread_ts + self._team_id = team_id + self._user_id = user_id + + def chat_stream( + self, + *, + channel: Optional[str] = None, + thread_ts: Optional[str] = None, + recipient_team_id: Optional[str] = None, + recipient_user_id: Optional[str] = None, + **kwargs, + ) -> ChatStream: + """Creates a ChatStream with defaults from event context. + + Each call creates a new instance. Create multiple for parallel streams. + + Args: + channel: Channel ID. Defaults to the channel from the event context. + thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. + recipient_team_id: Team ID of the recipient. Defaults to the team from the event context. + recipient_user_id: User ID of the recipient. Defaults to the user from the event context. + **kwargs: Additional arguments passed to ``WebClient.chat_stream()``. + + Returns: + A new ``ChatStream`` instance. + """ + provided = [arg for arg in (channel, thread_ts, recipient_team_id, recipient_user_id) if arg is not None] + if provided and len(provided) < 4: + raise ValueError( + "Either provide all of channel, thread_ts, recipient_team_id, and recipient_user_id, or none of them" + ) + # Argument validation is delegated to chat_stream() and the API + return self._client.chat_stream( + channel=channel or self._channel_id, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + recipient_team_id=recipient_team_id or self._team_id, + recipient_user_id=recipient_user_id or self._user_id, + **kwargs, + ) diff --git a/slack_bolt/agent/async_agent.py b/slack_bolt/agent/async_agent.py new file mode 100644 index 000000000..2ee15aa2e --- /dev/null +++ b/slack_bolt/agent/async_agent.py @@ -0,0 +1,70 @@ +from typing import Optional + +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_chat_stream import AsyncChatStream + + +class AsyncBoltAgent: + """Async agent listener argument for building AI-powered Slack agents. + + Experimental: + This API is experimental and may change in future releases. + + @app.event("app_mention") + async def handle_mention(agent): + stream = await agent.chat_stream() + await stream.append(markdown_text="Hello!") + await stream.stop() + """ + + def __init__( + self, + *, + client: AsyncWebClient, + channel_id: Optional[str] = None, + thread_ts: Optional[str] = None, + team_id: Optional[str] = None, + user_id: Optional[str] = None, + ): + self._client = client + self._channel_id = channel_id + self._thread_ts = thread_ts + self._team_id = team_id + self._user_id = user_id + + async def chat_stream( + self, + *, + channel: Optional[str] = None, + thread_ts: Optional[str] = None, + recipient_team_id: Optional[str] = None, + recipient_user_id: Optional[str] = None, + **kwargs, + ) -> AsyncChatStream: + """Creates an AsyncChatStream with defaults from event context. + + Each call creates a new instance. Create multiple for parallel streams. + + Args: + channel: Channel ID. Defaults to the channel from the event context. + thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. + recipient_team_id: Team ID of the recipient. Defaults to the team from the event context. + recipient_user_id: User ID of the recipient. Defaults to the user from the event context. + **kwargs: Additional arguments passed to ``AsyncWebClient.chat_stream()``. + + Returns: + A new ``AsyncChatStream`` instance. + """ + provided = [arg for arg in (channel, thread_ts, recipient_team_id, recipient_user_id) if arg is not None] + if provided and len(provided) < 4: + raise ValueError( + "Either provide all of channel, thread_ts, recipient_team_id, and recipient_user_id, or none of them" + ) + # Argument validation is delegated to chat_stream() and the API + return await self._client.chat_stream( + channel=channel or self._channel_id, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + recipient_team_id=recipient_team_id or self._team_id, + recipient_user_id=recipient_user_id or self._user_id, + **kwargs, + ) diff --git a/slack_bolt/kwargs_injection/args.py b/slack_bolt/kwargs_injection/args.py index 1a0ec3ca8..113e39c08 100644 --- a/slack_bolt/kwargs_injection/args.py +++ b/slack_bolt/kwargs_injection/args.py @@ -8,6 +8,7 @@ from slack_bolt.context.fail import Fail from slack_bolt.context.get_thread_context.get_thread_context import GetThreadContext from slack_bolt.context.respond import Respond +from slack_bolt.agent.agent import BoltAgent from slack_bolt.context.save_thread_context import SaveThreadContext from slack_bolt.context.say import Say from slack_bolt.context.set_status import SetStatus @@ -102,6 +103,8 @@ def handle_buttons(args): """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" + agent: Optional[BoltAgent] + """`agent` listener argument for AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -135,6 +138,7 @@ def __init__( set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, + agent: Optional[BoltAgent] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method @@ -168,6 +172,7 @@ def __init__( self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context + self.agent = agent self.next: Callable[[], None] = next self.next_: Callable[[], None] = next diff --git a/slack_bolt/kwargs_injection/async_args.py b/slack_bolt/kwargs_injection/async_args.py index 4953f2167..1f1dde024 100644 --- a/slack_bolt/kwargs_injection/async_args.py +++ b/slack_bolt/kwargs_injection/async_args.py @@ -1,6 +1,7 @@ from logging import Logger from typing import Callable, Awaitable, Dict, Any, Optional +from slack_bolt.agent.async_agent import AsyncBoltAgent from slack_bolt.context.ack.async_ack import AsyncAck from slack_bolt.context.async_context import AsyncBoltContext from slack_bolt.context.complete.async_complete import AsyncComplete @@ -101,6 +102,8 @@ async def handle_buttons(args): """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[AsyncSaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" + agent: Optional[AsyncBoltAgent] + """`agent` listener argument for AI Agents & Assistants""" # middleware next: Callable[[], Awaitable[None]] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -134,6 +137,7 @@ def __init__( set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] = None, get_thread_context: Optional[AsyncGetThreadContext] = None, save_thread_context: Optional[AsyncSaveThreadContext] = None, + agent: Optional[AsyncBoltAgent] = None, next: Callable[[], Awaitable[None]], **kwargs, # noqa ): @@ -164,6 +168,7 @@ def __init__( self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context + self.agent = agent self.next: Callable[[], Awaitable[None]] = next self.next_: Callable[[], Awaitable[None]] = next diff --git a/slack_bolt/kwargs_injection/async_utils.py b/slack_bolt/kwargs_injection/async_utils.py index c8870c3cc..e43cd0c27 100644 --- a/slack_bolt/kwargs_injection/async_utils.py +++ b/slack_bolt/kwargs_injection/async_utils.py @@ -1,9 +1,11 @@ import inspect import logging +import warnings from typing import Callable, Dict, MutableSequence, Optional, Any from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse +from slack_bolt.warning import ExperimentalWarning from .async_args import AsyncArgs from slack_bolt.request.payload_utils import ( to_options, @@ -29,7 +31,7 @@ def build_async_required_kwargs( error: Optional[Exception] = None, # for error handlers next_keys_required: bool = True, # False for listeners / middleware / error handlers ) -> Dict[str, Any]: - all_available_args = { + all_available_args: Dict[str, Any] = { "logger": logger, "client": request.context.client, "req": request, @@ -83,6 +85,23 @@ def build_async_required_kwargs( if k not in all_available_args: all_available_args[k] = v + # Defer agent creation to avoid constructing AsyncBoltAgent on every request + if "agent" in required_arg_names: + from slack_bolt.agent.async_agent import AsyncBoltAgent + + all_available_args["agent"] = AsyncBoltAgent( + client=request.context.client, + channel_id=request.context.channel_id, + thread_ts=request.context.thread_ts, + team_id=request.context.team_id, + user_id=request.context.user_id, + ) + warnings.warn( + "The agent listener argument is experimental and may change in future versions.", + category=ExperimentalWarning, + stacklevel=2, # Point to the caller, not this internal helper + ) + if len(required_arg_names) > 0: # To support instance/class methods in a class for listeners/middleware, # check if the first argument is either self or cls @@ -102,7 +121,7 @@ def build_async_required_kwargs( for name in required_arg_names: if name == "args": if isinstance(request, AsyncBoltRequest): - kwargs[name] = AsyncArgs(**all_available_args) # type: ignore[arg-type] + kwargs[name] = AsyncArgs(**all_available_args) else: logger.warning(f"Unknown Request object type detected ({type(request)})") diff --git a/slack_bolt/kwargs_injection/utils.py b/slack_bolt/kwargs_injection/utils.py index c1909c67a..73fe99bba 100644 --- a/slack_bolt/kwargs_injection/utils.py +++ b/slack_bolt/kwargs_injection/utils.py @@ -1,9 +1,11 @@ import inspect import logging +import warnings from typing import Callable, Dict, MutableSequence, Optional, Any from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse +from slack_bolt.warning import ExperimentalWarning from .args import Args from slack_bolt.request.payload_utils import ( to_options, @@ -29,7 +31,7 @@ def build_required_kwargs( error: Optional[Exception] = None, # for error handlers next_keys_required: bool = True, # False for listeners / middleware / error handlers ) -> Dict[str, Any]: - all_available_args = { + all_available_args: Dict[str, Any] = { "logger": logger, "client": request.context.client, "req": request, @@ -82,6 +84,23 @@ def build_required_kwargs( if k not in all_available_args: all_available_args[k] = v + # Defer agent creation to avoid constructing BoltAgent on every request + if "agent" in required_arg_names: + from slack_bolt.agent.agent import BoltAgent + + all_available_args["agent"] = BoltAgent( + client=request.context.client, + channel_id=request.context.channel_id, + thread_ts=request.context.thread_ts, + team_id=request.context.team_id, + user_id=request.context.user_id, + ) + warnings.warn( + "The agent listener argument is experimental and may change in future versions.", + category=ExperimentalWarning, + stacklevel=2, # Point to the caller, not this internal helper + ) + if len(required_arg_names) > 0: # To support instance/class methods in a class for listeners/middleware, # check if the first argument is either self or cls @@ -101,7 +120,7 @@ def build_required_kwargs( for name in required_arg_names: if name == "args": if isinstance(request, BoltRequest): - kwargs[name] = Args(**all_available_args) # type: ignore[arg-type] + kwargs[name] = Args(**all_available_args) else: logger.warning(f"Unknown Request object type detected ({type(request)})") diff --git a/slack_bolt/warning/__init__.py b/slack_bolt/warning/__init__.py new file mode 100644 index 000000000..df71b812f --- /dev/null +++ b/slack_bolt/warning/__init__.py @@ -0,0 +1,6 @@ +"""Bolt specific warning types.""" + + +class ExperimentalWarning(FutureWarning): + """Warning for features that are still in experimental phase.""" + pass diff --git a/tests/scenario_tests/test_events_agent.py b/tests/scenario_tests/test_events_agent.py new file mode 100644 index 000000000..667739728 --- /dev/null +++ b/tests/scenario_tests/test_events_agent.py @@ -0,0 +1,162 @@ +import json +from time import sleep + +import pytest +from slack_sdk.web import WebClient + +from slack_bolt import App, BoltRequest, BoltContext, BoltAgent +from slack_bolt.agent.agent import BoltAgent as BoltAgentDirect +from slack_bolt.warning import ExperimentalWarning +from tests.mock_web_api_server import ( + setup_mock_web_api_server, + cleanup_mock_web_api_server, +) +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestEventsAgent: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = WebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + def setup_method(self): + self.old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + restore_os_env(self.old_os_env) + + def test_agent_injected_for_app_mention(self): + app = App(client=self.web_client) + + state = {"called": False} + + def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @app.event("app_mention") + def handle_mention(agent: BoltAgent, context: BoltContext): + assert agent is not None + assert isinstance(agent, BoltAgentDirect) + assert context.channel_id == "C111" + state["called"] = True + + request = BoltRequest(body=app_mention_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + + def test_agent_available_in_action_listener(self): + app = App(client=self.web_client) + + state = {"called": False} + + def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @app.action("test_action") + def handle_action(ack, agent: BoltAgent): + ack() + assert agent is not None + assert isinstance(agent, BoltAgentDirect) + state["called"] = True + + request = BoltRequest(body=json.dumps(action_event_body), mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + + def test_agent_kwarg_emits_experimental_warning(self): + app = App(client=self.web_client) + + state = {"called": False} + + def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @app.event("app_mention") + def handle_mention(agent: BoltAgent): + state["called"] = True + + request = BoltRequest(body=app_mention_event_body, mode="socket_mode") + with pytest.warns(ExperimentalWarning, match="agent listener argument is experimental"): + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + + +# ---- Test event bodies ---- + + +def build_payload(event: dict) -> dict: + return { + "token": "verification_token", + "team_id": "T111", + "enterprise_id": "E111", + "api_app_id": "A111", + "event": event, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1599616881, + "authorizations": [ + { + "enterprise_id": "E111", + "team_id": "T111", + "user_id": "W111", + "is_bot": True, + "is_enterprise_install": False, + } + ], + } + + +app_mention_event_body = build_payload( + { + "type": "app_mention", + "user": "W222", + "text": "<@W111> hello", + "ts": "1234567890.123456", + "channel": "C111", + "event_ts": "1234567890.123456", + } +) + +action_event_body = { + "type": "block_actions", + "user": {"id": "W222", "username": "test_user", "name": "test_user", "team_id": "T111"}, + "api_app_id": "A111", + "token": "verification_token", + "container": {"type": "message", "message_ts": "1234567890.123456", "channel_id": "C111", "is_ephemeral": False}, + "channel": {"id": "C111", "name": "test-channel"}, + "team": {"id": "T111", "domain": "test"}, + "enterprise": {"id": "E111", "name": "test"}, + "trigger_id": "111.222.xxx", + "actions": [ + { + "type": "button", + "block_id": "b", + "action_id": "test_action", + "text": {"type": "plain_text", "text": "Button"}, + "action_ts": "1234567890.123456", + } + ], +} diff --git a/tests/scenario_tests_async/test_events_agent.py b/tests/scenario_tests_async/test_events_agent.py new file mode 100644 index 000000000..1702cdb61 --- /dev/null +++ b/tests/scenario_tests_async/test_events_agent.py @@ -0,0 +1,169 @@ +import asyncio +import json + +import pytest +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.agent.async_agent import AsyncBoltAgent +from slack_bolt.app.async_app import AsyncApp +from slack_bolt.context.async_context import AsyncBoltContext +from slack_bolt.request.async_request import AsyncBoltRequest +from slack_bolt.warning import ExperimentalWarning +from tests.mock_web_api_server import ( + cleanup_mock_web_api_server_async, + setup_mock_web_api_server_async, +) +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestAsyncEventsAgent: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = AsyncWebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) + try: + yield + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) + + @pytest.mark.asyncio + async def test_agent_injected_for_app_mention(self): + app = AsyncApp(client=self.web_client) + + state = {"called": False} + + async def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + await asyncio.sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @app.event("app_mention") + async def handle_mention(agent: AsyncBoltAgent, context: AsyncBoltContext): + assert agent is not None + assert isinstance(agent, AsyncBoltAgent) + assert context.channel_id == "C111" + state["called"] = True + + request = AsyncBoltRequest(body=app_mention_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + + @pytest.mark.asyncio + async def test_agent_available_in_action_listener(self): + app = AsyncApp(client=self.web_client) + + state = {"called": False} + + async def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + await asyncio.sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @app.action("test_action") + async def handle_action(ack, agent: AsyncBoltAgent): + await ack() + assert agent is not None + assert isinstance(agent, AsyncBoltAgent) + state["called"] = True + + request = AsyncBoltRequest(body=json.dumps(action_event_body), mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + + @pytest.mark.asyncio + async def test_agent_kwarg_emits_experimental_warning(self): + app = AsyncApp(client=self.web_client) + + state = {"called": False} + + async def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + await asyncio.sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @app.event("app_mention") + async def handle_mention(agent: AsyncBoltAgent): + state["called"] = True + + request = AsyncBoltRequest(body=app_mention_event_body, mode="socket_mode") + with pytest.warns(ExperimentalWarning, match="agent listener argument is experimental"): + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + + +# ---- Test event bodies ---- + + +def build_payload(event: dict) -> dict: + return { + "token": "verification_token", + "team_id": "T111", + "enterprise_id": "E111", + "api_app_id": "A111", + "event": event, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1599616881, + "authorizations": [ + { + "enterprise_id": "E111", + "team_id": "T111", + "user_id": "W111", + "is_bot": True, + "is_enterprise_install": False, + } + ], + } + + +app_mention_event_body = build_payload( + { + "type": "app_mention", + "user": "W222", + "text": "<@W111> hello", + "ts": "1234567890.123456", + "channel": "C111", + "event_ts": "1234567890.123456", + } +) + +action_event_body = { + "type": "block_actions", + "user": {"id": "W222", "username": "test_user", "name": "test_user", "team_id": "T111"}, + "api_app_id": "A111", + "token": "verification_token", + "container": {"type": "message", "message_ts": "1234567890.123456", "channel_id": "C111", "is_ephemeral": False}, + "channel": {"id": "C111", "name": "test-channel"}, + "team": {"id": "T111", "domain": "test"}, + "enterprise": {"id": "E111", "name": "test"}, + "trigger_id": "111.222.xxx", + "actions": [ + { + "type": "button", + "block_id": "b", + "action_id": "test_action", + "text": {"type": "plain_text", "text": "Button"}, + "action_ts": "1234567890.123456", + } + ], +} diff --git a/tests/slack_bolt/agent/__init__.py b/tests/slack_bolt/agent/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/slack_bolt/agent/test_agent.py b/tests/slack_bolt/agent/test_agent.py new file mode 100644 index 000000000..00e998379 --- /dev/null +++ b/tests/slack_bolt/agent/test_agent.py @@ -0,0 +1,103 @@ +from unittest.mock import MagicMock + +import pytest +from slack_sdk.web import WebClient +from slack_sdk.web.chat_stream import ChatStream + +from slack_bolt.agent.agent import BoltAgent + + +class TestBoltAgent: + def test_chat_stream_uses_context_defaults(self): + """BoltAgent.chat_stream() passes context defaults to WebClient.chat_stream().""" + client = MagicMock(spec=WebClient) + client.chat_stream.return_value = MagicMock(spec=ChatStream) + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + stream = agent.chat_stream() + + client.chat_stream.assert_called_once_with( + channel="C111", + thread_ts="1234567890.123456", + recipient_team_id="T111", + recipient_user_id="W222", + ) + assert stream is not None + + def test_chat_stream_overrides_context_defaults(self): + """Explicit kwargs to chat_stream() override context defaults.""" + client = MagicMock(spec=WebClient) + client.chat_stream.return_value = MagicMock(spec=ChatStream) + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + stream = agent.chat_stream( + channel="C999", + thread_ts="9999999999.999999", + recipient_team_id="T999", + recipient_user_id="U999", + ) + + client.chat_stream.assert_called_once_with( + channel="C999", + thread_ts="9999999999.999999", + recipient_team_id="T999", + recipient_user_id="U999", + ) + assert stream is not None + + def test_chat_stream_rejects_partial_overrides(self): + """Passing only some of the four context args raises ValueError.""" + client = MagicMock(spec=WebClient) + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + with pytest.raises(ValueError, match="Either provide all of"): + agent.chat_stream(channel="C999") + + def test_chat_stream_passes_extra_kwargs(self): + """Extra kwargs are forwarded to WebClient.chat_stream().""" + client = MagicMock(spec=WebClient) + client.chat_stream.return_value = MagicMock(spec=ChatStream) + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.chat_stream(buffer_size=512) + + client.chat_stream.assert_called_once_with( + channel="C111", + thread_ts="1234567890.123456", + recipient_team_id="T111", + recipient_user_id="W222", + buffer_size=512, + ) + + def test_import_from_slack_bolt(self): + from slack_bolt import BoltAgent as ImportedBoltAgent + + assert ImportedBoltAgent is BoltAgent + + def test_import_from_agent_module(self): + from slack_bolt.agent import BoltAgent as ImportedBoltAgent + + assert ImportedBoltAgent is BoltAgent diff --git a/tests/slack_bolt_async/agent/__init__.py b/tests/slack_bolt_async/agent/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/slack_bolt_async/agent/test_async_agent.py b/tests/slack_bolt_async/agent/test_async_agent.py new file mode 100644 index 000000000..02251fa4b --- /dev/null +++ b/tests/slack_bolt_async/agent/test_async_agent.py @@ -0,0 +1,114 @@ +from unittest.mock import MagicMock + +import pytest +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_chat_stream import AsyncChatStream + +from slack_bolt.agent.async_agent import AsyncBoltAgent + + +def _make_async_chat_stream_mock(): + mock_stream = MagicMock(spec=AsyncChatStream) + call_tracker = MagicMock() + + async def fake_chat_stream(**kwargs): + call_tracker(**kwargs) + return mock_stream + + return fake_chat_stream, call_tracker, mock_stream + + +class TestAsyncBoltAgent: + @pytest.mark.asyncio + async def test_chat_stream_uses_context_defaults(self): + """AsyncBoltAgent.chat_stream() passes context defaults to AsyncWebClient.chat_stream().""" + client = MagicMock(spec=AsyncWebClient) + client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + stream = await agent.chat_stream() + + call_tracker.assert_called_once_with( + channel="C111", + thread_ts="1234567890.123456", + recipient_team_id="T111", + recipient_user_id="W222", + ) + assert stream is not None + + @pytest.mark.asyncio + async def test_chat_stream_overrides_context_defaults(self): + """Explicit kwargs to chat_stream() override context defaults.""" + client = MagicMock(spec=AsyncWebClient) + client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + stream = await agent.chat_stream( + channel="C999", + thread_ts="9999999999.999999", + recipient_team_id="T999", + recipient_user_id="U999", + ) + + call_tracker.assert_called_once_with( + channel="C999", + thread_ts="9999999999.999999", + recipient_team_id="T999", + recipient_user_id="U999", + ) + assert stream is not None + + @pytest.mark.asyncio + async def test_chat_stream_rejects_partial_overrides(self): + """Passing only some of the four context args raises ValueError.""" + client = MagicMock(spec=AsyncWebClient) + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + with pytest.raises(ValueError, match="Either provide all of"): + await agent.chat_stream(channel="C999") + + @pytest.mark.asyncio + async def test_chat_stream_passes_extra_kwargs(self): + """Extra kwargs are forwarded to AsyncWebClient.chat_stream().""" + client = MagicMock(spec=AsyncWebClient) + client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.chat_stream(buffer_size=512) + + call_tracker.assert_called_once_with( + channel="C111", + thread_ts="1234567890.123456", + recipient_team_id="T111", + recipient_user_id="W222", + buffer_size=512, + ) + + @pytest.mark.asyncio + async def test_import_from_agent_module(self): + from slack_bolt.agent.async_agent import AsyncBoltAgent as ImportedAsyncBoltAgent + + assert ImportedAsyncBoltAgent is AsyncBoltAgent From cd52d19b034fbf19ce5949194b336aefe1e48ccb Mon Sep 17 00:00:00 2001 From: Ale Mercado <104795114+srtaalej@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:36:17 -0500 Subject: [PATCH 185/282] feat: add agent set status to BoltAgent (#1441) Co-authored-by: Eden Zimbelman --- slack_bolt/agent/agent.py | 32 ++++- slack_bolt/agent/async_agent.py | 33 ++++- tests/slack_bolt/agent/test_agent.py | 105 +++++++++++++++ .../agent/test_async_agent.py | 121 ++++++++++++++++++ 4 files changed, 288 insertions(+), 3 deletions(-) diff --git a/slack_bolt/agent/agent.py b/slack_bolt/agent/agent.py index db1c78aa9..3663b245b 100644 --- a/slack_bolt/agent/agent.py +++ b/slack_bolt/agent/agent.py @@ -1,6 +1,7 @@ -from typing import Optional +from typing import List, Optional from slack_sdk import WebClient +from slack_sdk.web import SlackResponse from slack_sdk.web.chat_stream import ChatStream @@ -71,3 +72,32 @@ def chat_stream( recipient_user_id=recipient_user_id or self._user_id, **kwargs, ) + + def set_status( + self, + *, + status: str, + loading_messages: Optional[List[str]] = None, + channel: Optional[str] = None, + thread_ts: Optional[str] = None, + **kwargs, + ) -> SlackResponse: + """Sets the status of an assistant thread. + + Args: + status: The status text to display. + loading_messages: Optional list of loading messages to cycle through. + channel: Channel ID. Defaults to the channel from the event context. + thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. + **kwargs: Additional arguments passed to ``WebClient.assistant_threads_setStatus()``. + + Returns: + ``SlackResponse`` from the API call. + """ + return self._client.assistant_threads_setStatus( + channel_id=channel or self._channel_id, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + status=status, + loading_messages=loading_messages, + **kwargs, + ) diff --git a/slack_bolt/agent/async_agent.py b/slack_bolt/agent/async_agent.py index 2ee15aa2e..5b86533e6 100644 --- a/slack_bolt/agent/async_agent.py +++ b/slack_bolt/agent/async_agent.py @@ -1,6 +1,6 @@ -from typing import Optional +from typing import List, Optional -from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_client import AsyncSlackResponse, AsyncWebClient from slack_sdk.web.async_chat_stream import AsyncChatStream @@ -68,3 +68,32 @@ async def chat_stream( recipient_user_id=recipient_user_id or self._user_id, **kwargs, ) + + async def set_status( + self, + *, + status: str, + loading_messages: Optional[List[str]] = None, + channel: Optional[str] = None, + thread_ts: Optional[str] = None, + **kwargs, + ) -> AsyncSlackResponse: + """Sets the status of an assistant thread. + + Args: + status: The status text to display. + loading_messages: Optional list of loading messages to cycle through. + channel: Channel ID. Defaults to the channel from the event context. + thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. + **kwargs: Additional arguments passed to ``AsyncWebClient.assistant_threads_setStatus()``. + + Returns: + ``AsyncSlackResponse`` from the API call. + """ + return await self._client.assistant_threads_setStatus( + channel_id=channel or self._channel_id, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + status=status, + loading_messages=loading_messages, + **kwargs, + ) diff --git a/tests/slack_bolt/agent/test_agent.py b/tests/slack_bolt/agent/test_agent.py index 00e998379..7dad481b0 100644 --- a/tests/slack_bolt/agent/test_agent.py +++ b/tests/slack_bolt/agent/test_agent.py @@ -92,6 +92,111 @@ def test_chat_stream_passes_extra_kwargs(self): buffer_size=512, ) + def test_set_status_uses_context_defaults(self): + """BoltAgent.set_status() passes context defaults to WebClient.assistant_threads_setStatus().""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setStatus.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_status(status="Thinking...") + + client.assistant_threads_setStatus.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + status="Thinking...", + loading_messages=None, + ) + + def test_set_status_with_loading_messages(self): + """BoltAgent.set_status() forwards loading_messages.""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setStatus.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_status( + status="Thinking...", + loading_messages=["Sitting...", "Waiting..."], + ) + + client.assistant_threads_setStatus.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + status="Thinking...", + loading_messages=["Sitting...", "Waiting..."], + ) + + def test_set_status_overrides_context_defaults(self): + """Explicit channel/thread_ts override context defaults.""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setStatus.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_status( + status="Thinking...", + channel="C999", + thread_ts="9999999999.999999", + ) + + client.assistant_threads_setStatus.assert_called_once_with( + channel_id="C999", + thread_ts="9999999999.999999", + status="Thinking...", + loading_messages=None, + ) + + def test_set_status_passes_extra_kwargs(self): + """Extra kwargs are forwarded to WebClient.assistant_threads_setStatus().""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setStatus.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_status(status="Thinking...", token="xoxb-override") + + client.assistant_threads_setStatus.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + status="Thinking...", + loading_messages=None, + token="xoxb-override", + ) + + def test_set_status_requires_status(self): + """set_status() raises TypeError when status is not provided.""" + client = MagicMock(spec=WebClient) + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + with pytest.raises(TypeError): + agent.set_status() + def test_import_from_slack_bolt(self): from slack_bolt import BoltAgent as ImportedBoltAgent diff --git a/tests/slack_bolt_async/agent/test_async_agent.py b/tests/slack_bolt_async/agent/test_async_agent.py index 02251fa4b..8e4c4d5c8 100644 --- a/tests/slack_bolt_async/agent/test_async_agent.py +++ b/tests/slack_bolt_async/agent/test_async_agent.py @@ -18,6 +18,17 @@ async def fake_chat_stream(**kwargs): return fake_chat_stream, call_tracker, mock_stream +def _make_async_api_mock(): + mock_response = MagicMock() + call_tracker = MagicMock() + + async def fake_api_call(**kwargs): + call_tracker(**kwargs) + return mock_response + + return fake_api_call, call_tracker, mock_response + + class TestAsyncBoltAgent: @pytest.mark.asyncio async def test_chat_stream_uses_context_defaults(self): @@ -107,6 +118,116 @@ async def test_chat_stream_passes_extra_kwargs(self): buffer_size=512, ) + @pytest.mark.asyncio + async def test_set_status_uses_context_defaults(self): + """AsyncBoltAgent.set_status() passes context defaults to AsyncWebClient.assistant_threads_setStatus().""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_status(status="Thinking...") + + call_tracker.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + status="Thinking...", + loading_messages=None, + ) + + @pytest.mark.asyncio + async def test_set_status_with_loading_messages(self): + """AsyncBoltAgent.set_status() forwards loading_messages.""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_status( + status="Thinking...", + loading_messages=["Sitting...", "Waiting..."], + ) + + call_tracker.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + status="Thinking...", + loading_messages=["Sitting...", "Waiting..."], + ) + + @pytest.mark.asyncio + async def test_set_status_overrides_context_defaults(self): + """Explicit channel/thread_ts override context defaults.""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_status( + status="Thinking...", + channel="C999", + thread_ts="9999999999.999999", + ) + + call_tracker.assert_called_once_with( + channel_id="C999", + thread_ts="9999999999.999999", + status="Thinking...", + loading_messages=None, + ) + + @pytest.mark.asyncio + async def test_set_status_passes_extra_kwargs(self): + """Extra kwargs are forwarded to AsyncWebClient.assistant_threads_setStatus().""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_status(status="Thinking...", token="xoxb-override") + + call_tracker.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + status="Thinking...", + loading_messages=None, + token="xoxb-override", + ) + + @pytest.mark.asyncio + async def test_set_status_requires_status(self): + """set_status() raises TypeError when status is not provided.""" + client = MagicMock(spec=AsyncWebClient) + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + with pytest.raises(TypeError): + await agent.set_status() + @pytest.mark.asyncio async def test_import_from_agent_module(self): from slack_bolt.agent.async_agent import AsyncBoltAgent as ImportedAsyncBoltAgent From 5cb618256c04ae53fe6a4cf1335a797a2395fee8 Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Wed, 18 Feb 2026 10:49:43 -0600 Subject: [PATCH 186/282] Docs: Add headings so copy as markdown button shows up (#1443) --- docs/english/concepts/acknowledge.md | 3 +++ docs/english/concepts/adapters.md | 2 ++ docs/english/concepts/app-home.md | 3 +++ docs/english/concepts/authorization.md | 2 ++ docs/english/concepts/commands.md | 3 +++ docs/english/concepts/context.md | 2 ++ docs/english/concepts/custom-adapters.md | 2 ++ docs/english/concepts/errors.md | 2 ++ docs/english/concepts/global-middleware.md | 3 +++ docs/english/concepts/listener-middleware.md | 2 ++ docs/english/concepts/logging.md | 2 ++ docs/english/concepts/opening-modals.md | 2 ++ docs/english/concepts/select-menu-options.md | 3 +++ docs/english/concepts/web-api.md | 2 ++ 14 files changed, 33 insertions(+) diff --git a/docs/english/concepts/acknowledge.md b/docs/english/concepts/acknowledge.md index 7d91e0851..57b346bd3 100644 --- a/docs/english/concepts/acknowledge.md +++ b/docs/english/concepts/acknowledge.md @@ -11,6 +11,9 @@ We recommend calling `ack()` right away before initiating any time-consuming pro ::: Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. + +## Example + ```python # Example of responding to an external_select options request @app.options("menu_selection") diff --git a/docs/english/concepts/adapters.md b/docs/english/concepts/adapters.md index 321dae0ab..ad43a27da 100644 --- a/docs/english/concepts/adapters.md +++ b/docs/english/concepts/adapters.md @@ -8,6 +8,8 @@ To use an adapter, you'll create an app with the framework of your choosing and The full list adapters, as well as configuration and sample usage, can be found within the repository's [`examples`](https://github.com/slackapi/bolt-python/tree/main/examples) +## Example + ```python from slack_bolt import App app = App( diff --git a/docs/english/concepts/app-home.md b/docs/english/concepts/app-home.md index 8b0e2cf11..f4f15337f 100644 --- a/docs/english/concepts/app-home.md +++ b/docs/english/concepts/app-home.md @@ -5,6 +5,9 @@ You can subscribe to the [`app_home_opened`](/reference/events/app_home_opened) event to listen for when users open your App Home. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. + +## Example + ```python @app.event("app_home_opened") def update_home_tab(client, event, logger): diff --git a/docs/english/concepts/authorization.md b/docs/english/concepts/authorization.md index 242a86b39..f6a258491 100644 --- a/docs/english/concepts/authorization.md +++ b/docs/english/concepts/authorization.md @@ -12,6 +12,8 @@ For a more custom solution, you can set the `authorize` parameter to a function - **`enterprise_id`** and **`team_id`**, which can be found in requests sent to your app. - **`user_id`** only when using `user_token`. +## Example + ```python import os from slack_bolt import App diff --git a/docs/english/concepts/commands.md b/docs/english/concepts/commands.md index 81167fb83..cd772c57b 100644 --- a/docs/english/concepts/commands.md +++ b/docs/english/concepts/commands.md @@ -9,6 +9,9 @@ There are two ways to respond to slash commands. The first way is to use `say()` When setting up commands within your app configuration, you'll append `/slack/events` to your request URL. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. + +## Example + ```python # The echo command simply echoes on command @app.command("/echo") diff --git a/docs/english/concepts/context.md b/docs/english/concepts/context.md index fb134c896..46684ea28 100644 --- a/docs/english/concepts/context.md +++ b/docs/english/concepts/context.md @@ -4,6 +4,8 @@ All listeners have access to a `context` dictionary, which can be used to enrich `context` is just a dictionary, so you can directly modify it. +## Example + ```python # Listener middleware to fetch tasks from external system using user ID def fetch_tasks(context, event, next): diff --git a/docs/english/concepts/custom-adapters.md b/docs/english/concepts/custom-adapters.md index 62532e7cd..21f7f33e0 100644 --- a/docs/english/concepts/custom-adapters.md +++ b/docs/english/concepts/custom-adapters.md @@ -18,6 +18,8 @@ Your adapter will return [an instance of `BoltResponse`](https://github.com/slac For more in-depth examples of custom adapters, look at the implementations of the [built-in adapters](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter). +## Example + ```python # Necessary imports for Flask from flask import Request, Response, make_response diff --git a/docs/english/concepts/errors.md b/docs/english/concepts/errors.md index ed41c5816..7b40adb7f 100644 --- a/docs/english/concepts/errors.md +++ b/docs/english/concepts/errors.md @@ -4,6 +4,8 @@ If an error occurs in a listener, you can handle it directly using a try/except 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. +## Example + ```python @app.error def custom_error_handler(error, body, logger): diff --git a/docs/english/concepts/global-middleware.md b/docs/english/concepts/global-middleware.md index dbcdeae99..7b7bdb059 100644 --- a/docs/english/concepts/global-middleware.md +++ b/docs/english/concepts/global-middleware.md @@ -5,6 +5,9 @@ Global middleware is run for all incoming requests, before any listener middlewa Both global and listener middleware must call `next()` to pass control of the execution chain to the next middleware. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. + +## Example + ```python @app.use def auth_acme(client, context, logger, payload, next): diff --git a/docs/english/concepts/listener-middleware.md b/docs/english/concepts/listener-middleware.md index c8bfc964e..dd020373f 100644 --- a/docs/english/concepts/listener-middleware.md +++ b/docs/english/concepts/listener-middleware.md @@ -6,6 +6,8 @@ If your listener middleware is a quite simple one, you can use a listener matche Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. +## Example + ```python # Listener middleware which filters out messages from a bot def no_bot_messages(message, next): diff --git a/docs/english/concepts/logging.md b/docs/english/concepts/logging.md index 49e275d2d..599431550 100644 --- a/docs/english/concepts/logging.md +++ b/docs/english/concepts/logging.md @@ -4,6 +4,8 @@ By default, Bolt will log information from your app to the output destination. A 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. +## Example + ```python import logging diff --git a/docs/english/concepts/opening-modals.md b/docs/english/concepts/opening-modals.md index 1f053539f..01716f613 100644 --- a/docs/english/concepts/opening-modals.md +++ b/docs/english/concepts/opening-modals.md @@ -8,6 +8,8 @@ Read more about modal composition in the [API documentation](/surfaces/modals#co Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. +## Example + ```python # Listen for a shortcut invocation @app.shortcut("open_modal") diff --git a/docs/english/concepts/select-menu-options.md b/docs/english/concepts/select-menu-options.md index 40d29472c..8e6cbb9fe 100644 --- a/docs/english/concepts/select-menu-options.md +++ b/docs/english/concepts/select-menu-options.md @@ -10,6 +10,9 @@ To respond to options requests, you'll need to call `ack()` with a valid `option Additionally, you may want to apply filtering logic to the returned options based on user input. This can be accomplished by using the `payload` argument to your options listener and checking for the contents of the `value` property within it. Based on the `value` you can return different options. All listeners and middleware handlers in Bolt for Python have access to [many useful arguments](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) - be sure to check them out! Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. + +## Example + ```python # Example of responding to an external_select options request @app.options("external_action") diff --git a/docs/english/concepts/web-api.md b/docs/english/concepts/web-api.md index 9cf436851..81f7c9b60 100644 --- a/docs/english/concepts/web-api.md +++ b/docs/english/concepts/web-api.md @@ -8,6 +8,8 @@ The token used to initialize Bolt can be found in the `context` object, which is ::: +## Example + ```python @app.message("wake me up") def say_hello(client, message): From 92bff603ef91d590da0849a6a1c4af319bad4996 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 18 Feb 2026 18:24:50 -0800 Subject: [PATCH 187/282] feat(agent): add set_suggested_prompts helper (#1442) Co-authored-by: Ale Mercado --- slack_bolt/agent/agent.py | 39 +++++- slack_bolt/agent/async_agent.py | 39 +++++- tests/slack_bolt/agent/test_agent.py | 112 +++++++++++++++++ .../agent/test_async_agent.py | 117 ++++++++++++++++++ 4 files changed, 305 insertions(+), 2 deletions(-) diff --git a/slack_bolt/agent/agent.py b/slack_bolt/agent/agent.py index 3663b245b..056dba986 100644 --- a/slack_bolt/agent/agent.py +++ b/slack_bolt/agent/agent.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Dict, List, Optional, Sequence, Union from slack_sdk import WebClient from slack_sdk.web import SlackResponse @@ -101,3 +101,40 @@ def set_status( loading_messages=loading_messages, **kwargs, ) + + def set_suggested_prompts( + self, + *, + prompts: Sequence[Union[str, Dict[str, str]]], + title: Optional[str] = None, + channel: Optional[str] = None, + thread_ts: Optional[str] = None, + **kwargs, + ) -> SlackResponse: + """Sets suggested prompts for an assistant thread. + + Args: + prompts: A sequence of prompts. Each prompt can be either a string + (used as both title and message) or a dict with 'title' and 'message' keys. + title: Optional title for the suggested prompts section. + channel: Channel ID. Defaults to the channel from the event context. + thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. + **kwargs: Additional arguments passed to ``WebClient.assistant_threads_setSuggestedPrompts()``. + + Returns: + ``SlackResponse`` from the API call. + """ + prompts_arg: List[Dict[str, str]] = [] + for prompt in prompts: + if isinstance(prompt, str): + prompts_arg.append({"title": prompt, "message": prompt}) + else: + prompts_arg.append(prompt) + + return self._client.assistant_threads_setSuggestedPrompts( + channel_id=channel or self._channel_id, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + prompts=prompts_arg, + title=title, + **kwargs, + ) diff --git a/slack_bolt/agent/async_agent.py b/slack_bolt/agent/async_agent.py index 5b86533e6..5630e1b81 100644 --- a/slack_bolt/agent/async_agent.py +++ b/slack_bolt/agent/async_agent.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Dict, List, Optional, Sequence, Union from slack_sdk.web.async_client import AsyncSlackResponse, AsyncWebClient from slack_sdk.web.async_chat_stream import AsyncChatStream @@ -97,3 +97,40 @@ async def set_status( loading_messages=loading_messages, **kwargs, ) + + async def set_suggested_prompts( + self, + *, + prompts: Sequence[Union[str, Dict[str, str]]], + title: Optional[str] = None, + channel: Optional[str] = None, + thread_ts: Optional[str] = None, + **kwargs, + ) -> AsyncSlackResponse: + """Sets suggested prompts for an assistant thread. + + Args: + prompts: A sequence of prompts. Each prompt can be either a string + (used as both title and message) or a dict with 'title' and 'message' keys. + title: Optional title for the suggested prompts section. + channel: Channel ID. Defaults to the channel from the event context. + thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. + **kwargs: Additional arguments passed to ``AsyncWebClient.assistant_threads_setSuggestedPrompts()``. + + Returns: + ``AsyncSlackResponse`` from the API call. + """ + prompts_arg: List[Dict[str, str]] = [] + for prompt in prompts: + if isinstance(prompt, str): + prompts_arg.append({"title": prompt, "message": prompt}) + else: + prompts_arg.append(prompt) + + return await self._client.assistant_threads_setSuggestedPrompts( + channel_id=channel or self._channel_id, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + prompts=prompts_arg, + title=title, + **kwargs, + ) diff --git a/tests/slack_bolt/agent/test_agent.py b/tests/slack_bolt/agent/test_agent.py index 7dad481b0..1d14eda06 100644 --- a/tests/slack_bolt/agent/test_agent.py +++ b/tests/slack_bolt/agent/test_agent.py @@ -197,6 +197,118 @@ def test_set_status_requires_status(self): with pytest.raises(TypeError): agent.set_status() + def test_set_suggested_prompts_uses_context_defaults(self): + """BoltAgent.set_suggested_prompts() passes context defaults to WebClient.assistant_threads_setSuggestedPrompts().""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_suggested_prompts(prompts=["What can you do?", "Help me write code"]) + + client.assistant_threads_setSuggestedPrompts.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + prompts=[ + {"title": "What can you do?", "message": "What can you do?"}, + {"title": "Help me write code", "message": "Help me write code"}, + ], + title=None, + ) + + def test_set_suggested_prompts_with_dict_prompts(self): + """BoltAgent.set_suggested_prompts() accepts dict prompts with title and message.""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_suggested_prompts( + prompts=[ + {"title": "Short title", "message": "A much longer message for this prompt"}, + ], + title="Suggestions", + ) + + client.assistant_threads_setSuggestedPrompts.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + prompts=[ + {"title": "Short title", "message": "A much longer message for this prompt"}, + ], + title="Suggestions", + ) + + def test_set_suggested_prompts_overrides_context_defaults(self): + """Explicit channel/thread_ts override context defaults.""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_suggested_prompts( + prompts=["Hello"], + channel="C999", + thread_ts="9999999999.999999", + ) + + client.assistant_threads_setSuggestedPrompts.assert_called_once_with( + channel_id="C999", + thread_ts="9999999999.999999", + prompts=[{"title": "Hello", "message": "Hello"}], + title=None, + ) + + def test_set_suggested_prompts_passes_extra_kwargs(self): + """Extra kwargs are forwarded to WebClient.assistant_threads_setSuggestedPrompts().""" + client = MagicMock(spec=WebClient) + client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() + + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + agent.set_suggested_prompts(prompts=["Hello"], token="xoxb-override") + + client.assistant_threads_setSuggestedPrompts.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + prompts=[{"title": "Hello", "message": "Hello"}], + title=None, + token="xoxb-override", + ) + + def test_set_suggested_prompts_requires_prompts(self): + """set_suggested_prompts() raises TypeError when prompts is not provided.""" + client = MagicMock(spec=WebClient) + agent = BoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + with pytest.raises(TypeError): + agent.set_suggested_prompts() + def test_import_from_slack_bolt(self): from slack_bolt import BoltAgent as ImportedBoltAgent diff --git a/tests/slack_bolt_async/agent/test_async_agent.py b/tests/slack_bolt_async/agent/test_async_agent.py index 8e4c4d5c8..b934bbaeb 100644 --- a/tests/slack_bolt_async/agent/test_async_agent.py +++ b/tests/slack_bolt_async/agent/test_async_agent.py @@ -228,6 +228,123 @@ async def test_set_status_requires_status(self): with pytest.raises(TypeError): await agent.set_status() + @pytest.mark.asyncio + async def test_set_suggested_prompts_uses_context_defaults(self): + """AsyncBoltAgent.set_suggested_prompts() passes context defaults to AsyncWebClient.assistant_threads_setSuggestedPrompts().""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_suggested_prompts(prompts=["What can you do?", "Help me write code"]) + + call_tracker.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + prompts=[ + {"title": "What can you do?", "message": "What can you do?"}, + {"title": "Help me write code", "message": "Help me write code"}, + ], + title=None, + ) + + @pytest.mark.asyncio + async def test_set_suggested_prompts_with_dict_prompts(self): + """AsyncBoltAgent.set_suggested_prompts() accepts dict prompts with title and message.""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_suggested_prompts( + prompts=[ + {"title": "Short title", "message": "A much longer message for this prompt"}, + ], + title="Suggestions", + ) + + call_tracker.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + prompts=[ + {"title": "Short title", "message": "A much longer message for this prompt"}, + ], + title="Suggestions", + ) + + @pytest.mark.asyncio + async def test_set_suggested_prompts_overrides_context_defaults(self): + """Explicit channel/thread_ts override context defaults.""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_suggested_prompts( + prompts=["Hello"], + channel="C999", + thread_ts="9999999999.999999", + ) + + call_tracker.assert_called_once_with( + channel_id="C999", + thread_ts="9999999999.999999", + prompts=[{"title": "Hello", "message": "Hello"}], + title=None, + ) + + @pytest.mark.asyncio + async def test_set_suggested_prompts_passes_extra_kwargs(self): + """Extra kwargs are forwarded to AsyncWebClient.assistant_threads_setSuggestedPrompts().""" + client = MagicMock(spec=AsyncWebClient) + client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + await agent.set_suggested_prompts(prompts=["Hello"], token="xoxb-override") + + call_tracker.assert_called_once_with( + channel_id="C111", + thread_ts="1234567890.123456", + prompts=[{"title": "Hello", "message": "Hello"}], + title=None, + token="xoxb-override", + ) + + @pytest.mark.asyncio + async def test_set_suggested_prompts_requires_prompts(self): + """set_suggested_prompts() raises TypeError when prompts is not provided.""" + client = MagicMock(spec=AsyncWebClient) + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + thread_ts="1234567890.123456", + team_id="T111", + user_id="W222", + ) + with pytest.raises(TypeError): + await agent.set_suggested_prompts() + @pytest.mark.asyncio async def test_import_from_agent_module(self): from slack_bolt.agent.async_agent import AsyncBoltAgent as ImportedAsyncBoltAgent From d789facab62a77c37e0dc0f34609a0a91253230c Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 18 Feb 2026 18:34:54 -0800 Subject: [PATCH 188/282] feat(agent): default to message 'ts' when no 'thread_ts' is avaialble for 'agent.chat_stream(...)' (#1444) Co-authored-by: Ale Mercado --- slack_bolt/agent/agent.py | 11 ++--- slack_bolt/agent/async_agent.py | 8 ++-- slack_bolt/kwargs_injection/async_utils.py | 5 +- slack_bolt/kwargs_injection/utils.py | 5 +- slack_bolt/request/internals.py | 3 ++ tests/slack_bolt/agent/test_agent.py | 45 ++++++++++++++++++ .../agent/test_async_agent.py | 47 +++++++++++++++++++ 7 files changed, 113 insertions(+), 11 deletions(-) diff --git a/slack_bolt/agent/agent.py b/slack_bolt/agent/agent.py index 056dba986..aa84bae90 100644 --- a/slack_bolt/agent/agent.py +++ b/slack_bolt/agent/agent.py @@ -11,9 +11,6 @@ class BoltAgent: Experimental: This API is experimental and may change in future releases. - FIXME: chat_stream() only works when thread_ts is available (DMs and threaded replies). - It does not work on channel messages because ts is not provided to BoltAgent yet. - @app.event("app_mention") def handle_mention(agent): stream = agent.chat_stream() @@ -27,12 +24,14 @@ def __init__( client: WebClient, channel_id: Optional[str] = None, thread_ts: Optional[str] = None, + ts: Optional[str] = None, team_id: Optional[str] = None, user_id: Optional[str] = None, ): self._client = client self._channel_id = channel_id self._thread_ts = thread_ts + self._ts = ts self._team_id = team_id self._user_id = user_id @@ -67,7 +66,7 @@ def chat_stream( # Argument validation is delegated to chat_stream() and the API return self._client.chat_stream( channel=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] recipient_team_id=recipient_team_id or self._team_id, recipient_user_id=recipient_user_id or self._user_id, **kwargs, @@ -96,7 +95,7 @@ def set_status( """ return self._client.assistant_threads_setStatus( channel_id=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] status=status, loading_messages=loading_messages, **kwargs, @@ -133,7 +132,7 @@ def set_suggested_prompts( return self._client.assistant_threads_setSuggestedPrompts( channel_id=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] prompts=prompts_arg, title=title, **kwargs, diff --git a/slack_bolt/agent/async_agent.py b/slack_bolt/agent/async_agent.py index 5630e1b81..7272338e1 100644 --- a/slack_bolt/agent/async_agent.py +++ b/slack_bolt/agent/async_agent.py @@ -23,12 +23,14 @@ def __init__( client: AsyncWebClient, channel_id: Optional[str] = None, thread_ts: Optional[str] = None, + ts: Optional[str] = None, team_id: Optional[str] = None, user_id: Optional[str] = None, ): self._client = client self._channel_id = channel_id self._thread_ts = thread_ts + self._ts = ts self._team_id = team_id self._user_id = user_id @@ -63,7 +65,7 @@ async def chat_stream( # Argument validation is delegated to chat_stream() and the API return await self._client.chat_stream( channel=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] recipient_team_id=recipient_team_id or self._team_id, recipient_user_id=recipient_user_id or self._user_id, **kwargs, @@ -92,7 +94,7 @@ async def set_status( """ return await self._client.assistant_threads_setStatus( channel_id=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] status=status, loading_messages=loading_messages, **kwargs, @@ -129,7 +131,7 @@ async def set_suggested_prompts( return await self._client.assistant_threads_setSuggestedPrompts( channel_id=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts, # type: ignore[arg-type] + thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] prompts=prompts_arg, title=title, **kwargs, diff --git a/slack_bolt/kwargs_injection/async_utils.py b/slack_bolt/kwargs_injection/async_utils.py index e43cd0c27..aa84b2d11 100644 --- a/slack_bolt/kwargs_injection/async_utils.py +++ b/slack_bolt/kwargs_injection/async_utils.py @@ -89,10 +89,13 @@ def build_async_required_kwargs( if "agent" in required_arg_names: from slack_bolt.agent.async_agent import AsyncBoltAgent + event = request.body.get("event", {}) + all_available_args["agent"] = AsyncBoltAgent( client=request.context.client, channel_id=request.context.channel_id, - thread_ts=request.context.thread_ts, + thread_ts=request.context.thread_ts or event.get("thread_ts"), + ts=event.get("ts"), team_id=request.context.team_id, user_id=request.context.user_id, ) diff --git a/slack_bolt/kwargs_injection/utils.py b/slack_bolt/kwargs_injection/utils.py index 73fe99bba..5cd410a07 100644 --- a/slack_bolt/kwargs_injection/utils.py +++ b/slack_bolt/kwargs_injection/utils.py @@ -88,10 +88,13 @@ def build_required_kwargs( if "agent" in required_arg_names: from slack_bolt.agent.agent import BoltAgent + event = request.body.get("event", {}) + all_available_args["agent"] = BoltAgent( client=request.context.client, channel_id=request.context.channel_id, - thread_ts=request.context.thread_ts, + thread_ts=request.context.thread_ts or event.get("thread_ts"), + ts=event.get("ts"), team_id=request.context.team_id, user_id=request.context.user_id, ) diff --git a/slack_bolt/request/internals.py b/slack_bolt/request/internals.py index 014a8134a..e6a32db0d 100644 --- a/slack_bolt/request/internals.py +++ b/slack_bolt/request/internals.py @@ -218,6 +218,9 @@ def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]: # This utility initially supports only the use cases for AI assistants, but it may be fine to add more patterns. # That said, note that thread_ts is always required for assistant threads, but it's not for channels. # Thus, blindly setting this thread_ts to say utility can break existing apps' behaviors. + # + # The BoltAgent class handles non-assistant thread_ts separately by reading from the event directly, + # allowing it to work correctly without affecting say() behavior. if is_assistant_event(payload): event = payload["event"] if ( diff --git a/tests/slack_bolt/agent/test_agent.py b/tests/slack_bolt/agent/test_agent.py index 1d14eda06..87d51d9eb 100644 --- a/tests/slack_bolt/agent/test_agent.py +++ b/tests/slack_bolt/agent/test_agent.py @@ -92,6 +92,51 @@ def test_chat_stream_passes_extra_kwargs(self): buffer_size=512, ) + def test_chat_stream_falls_back_to_ts(self): + """When thread_ts is not set, chat_stream() falls back to ts.""" + client = MagicMock(spec=WebClient) + client.chat_stream.return_value = MagicMock(spec=ChatStream) + + agent = BoltAgent( + client=client, + channel_id="C111", + team_id="T111", + ts="1111111111.111111", + user_id="W222", + ) + stream = agent.chat_stream() + + client.chat_stream.assert_called_once_with( + channel="C111", + thread_ts="1111111111.111111", + recipient_team_id="T111", + recipient_user_id="W222", + ) + assert stream is not None + + def test_chat_stream_prefers_thread_ts_over_ts(self): + """thread_ts takes priority over ts.""" + client = MagicMock(spec=WebClient) + client.chat_stream.return_value = MagicMock(spec=ChatStream) + + agent = BoltAgent( + client=client, + channel_id="C111", + team_id="T111", + thread_ts="1234567890.123456", + ts="1111111111.111111", + user_id="W222", + ) + stream = agent.chat_stream() + + client.chat_stream.assert_called_once_with( + channel="C111", + thread_ts="1234567890.123456", + recipient_team_id="T111", + recipient_user_id="W222", + ) + assert stream is not None + def test_set_status_uses_context_defaults(self): """BoltAgent.set_status() passes context defaults to WebClient.assistant_threads_setStatus().""" client = MagicMock(spec=WebClient) diff --git a/tests/slack_bolt_async/agent/test_async_agent.py b/tests/slack_bolt_async/agent/test_async_agent.py index b934bbaeb..7c01a4301 100644 --- a/tests/slack_bolt_async/agent/test_async_agent.py +++ b/tests/slack_bolt_async/agent/test_async_agent.py @@ -118,6 +118,53 @@ async def test_chat_stream_passes_extra_kwargs(self): buffer_size=512, ) + @pytest.mark.asyncio + async def test_chat_stream_falls_back_to_ts(self): + """When thread_ts is not set, chat_stream() falls back to ts.""" + client = MagicMock(spec=AsyncWebClient) + client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + team_id="T111", + ts="1111111111.111111", + user_id="W222", + ) + stream = await agent.chat_stream() + + call_tracker.assert_called_once_with( + channel="C111", + thread_ts="1111111111.111111", + recipient_team_id="T111", + recipient_user_id="W222", + ) + assert stream is not None + + @pytest.mark.asyncio + async def test_chat_stream_prefers_thread_ts_over_ts(self): + """thread_ts takes priority over ts.""" + client = MagicMock(spec=AsyncWebClient) + client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() + + agent = AsyncBoltAgent( + client=client, + channel_id="C111", + team_id="T111", + thread_ts="1234567890.123456", + ts="1111111111.111111", + user_id="W222", + ) + stream = await agent.chat_stream() + + call_tracker.assert_called_once_with( + channel="C111", + thread_ts="1234567890.123456", + recipient_team_id="T111", + recipient_user_id="W222", + ) + assert stream is not None + @pytest.mark.asyncio async def test_set_status_uses_context_defaults(self): """AsyncBoltAgent.set_status() passes context defaults to AsyncWebClient.assistant_threads_setStatus().""" From 837e120a3119f6b92da1dd52064a2400e9f97ba9 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 19 Feb 2026 11:28:37 -0800 Subject: [PATCH 189/282] fix(agent): match channel_id api argument for set_status and set_suggested_prompts (#1446) --- slack_bolt/agent/agent.py | 12 ++++++------ slack_bolt/agent/async_agent.py | 12 ++++++------ tests/slack_bolt/agent/test_agent.py | 8 ++++---- tests/slack_bolt_async/agent/test_async_agent.py | 8 ++++---- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/slack_bolt/agent/agent.py b/slack_bolt/agent/agent.py index aa84bae90..523b0e33c 100644 --- a/slack_bolt/agent/agent.py +++ b/slack_bolt/agent/agent.py @@ -77,7 +77,7 @@ def set_status( *, status: str, loading_messages: Optional[List[str]] = None, - channel: Optional[str] = None, + channel_id: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> SlackResponse: @@ -86,7 +86,7 @@ def set_status( Args: status: The status text to display. loading_messages: Optional list of loading messages to cycle through. - channel: Channel ID. Defaults to the channel from the event context. + channel_id: Channel ID. Defaults to the channel from the event context. thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. **kwargs: Additional arguments passed to ``WebClient.assistant_threads_setStatus()``. @@ -94,7 +94,7 @@ def set_status( ``SlackResponse`` from the API call. """ return self._client.assistant_threads_setStatus( - channel_id=channel or self._channel_id, # type: ignore[arg-type] + channel_id=channel_id or self._channel_id, # type: ignore[arg-type] thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] status=status, loading_messages=loading_messages, @@ -106,7 +106,7 @@ def set_suggested_prompts( *, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, - channel: Optional[str] = None, + channel_id: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> SlackResponse: @@ -116,7 +116,7 @@ def set_suggested_prompts( prompts: A sequence of prompts. Each prompt can be either a string (used as both title and message) or a dict with 'title' and 'message' keys. title: Optional title for the suggested prompts section. - channel: Channel ID. Defaults to the channel from the event context. + channel_id: Channel ID. Defaults to the channel from the event context. thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. **kwargs: Additional arguments passed to ``WebClient.assistant_threads_setSuggestedPrompts()``. @@ -131,7 +131,7 @@ def set_suggested_prompts( prompts_arg.append(prompt) return self._client.assistant_threads_setSuggestedPrompts( - channel_id=channel or self._channel_id, # type: ignore[arg-type] + channel_id=channel_id or self._channel_id, # type: ignore[arg-type] thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] prompts=prompts_arg, title=title, diff --git a/slack_bolt/agent/async_agent.py b/slack_bolt/agent/async_agent.py index 7272338e1..da4ec6c0a 100644 --- a/slack_bolt/agent/async_agent.py +++ b/slack_bolt/agent/async_agent.py @@ -76,7 +76,7 @@ async def set_status( *, status: str, loading_messages: Optional[List[str]] = None, - channel: Optional[str] = None, + channel_id: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> AsyncSlackResponse: @@ -85,7 +85,7 @@ async def set_status( Args: status: The status text to display. loading_messages: Optional list of loading messages to cycle through. - channel: Channel ID. Defaults to the channel from the event context. + channel_id: Channel ID. Defaults to the channel from the event context. thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. **kwargs: Additional arguments passed to ``AsyncWebClient.assistant_threads_setStatus()``. @@ -93,7 +93,7 @@ async def set_status( ``AsyncSlackResponse`` from the API call. """ return await self._client.assistant_threads_setStatus( - channel_id=channel or self._channel_id, # type: ignore[arg-type] + channel_id=channel_id or self._channel_id, # type: ignore[arg-type] thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] status=status, loading_messages=loading_messages, @@ -105,7 +105,7 @@ async def set_suggested_prompts( *, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, - channel: Optional[str] = None, + channel_id: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> AsyncSlackResponse: @@ -115,7 +115,7 @@ async def set_suggested_prompts( prompts: A sequence of prompts. Each prompt can be either a string (used as both title and message) or a dict with 'title' and 'message' keys. title: Optional title for the suggested prompts section. - channel: Channel ID. Defaults to the channel from the event context. + channel_id: Channel ID. Defaults to the channel from the event context. thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. **kwargs: Additional arguments passed to ``AsyncWebClient.assistant_threads_setSuggestedPrompts()``. @@ -130,7 +130,7 @@ async def set_suggested_prompts( prompts_arg.append(prompt) return await self._client.assistant_threads_setSuggestedPrompts( - channel_id=channel or self._channel_id, # type: ignore[arg-type] + channel_id=channel_id or self._channel_id, # type: ignore[arg-type] thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] prompts=prompts_arg, title=title, diff --git a/tests/slack_bolt/agent/test_agent.py b/tests/slack_bolt/agent/test_agent.py index 87d51d9eb..76ac7d17b 100644 --- a/tests/slack_bolt/agent/test_agent.py +++ b/tests/slack_bolt/agent/test_agent.py @@ -183,7 +183,7 @@ def test_set_status_with_loading_messages(self): ) def test_set_status_overrides_context_defaults(self): - """Explicit channel/thread_ts override context defaults.""" + """Explicit channel_id/thread_ts override context defaults.""" client = MagicMock(spec=WebClient) client.assistant_threads_setStatus.return_value = MagicMock() @@ -196,7 +196,7 @@ def test_set_status_overrides_context_defaults(self): ) agent.set_status( status="Thinking...", - channel="C999", + channel_id="C999", thread_ts="9999999999.999999", ) @@ -295,7 +295,7 @@ def test_set_suggested_prompts_with_dict_prompts(self): ) def test_set_suggested_prompts_overrides_context_defaults(self): - """Explicit channel/thread_ts override context defaults.""" + """Explicit channel_id/thread_ts override context defaults.""" client = MagicMock(spec=WebClient) client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() @@ -308,7 +308,7 @@ def test_set_suggested_prompts_overrides_context_defaults(self): ) agent.set_suggested_prompts( prompts=["Hello"], - channel="C999", + channel_id="C999", thread_ts="9999999999.999999", ) diff --git a/tests/slack_bolt_async/agent/test_async_agent.py b/tests/slack_bolt_async/agent/test_async_agent.py index 7c01a4301..3ed8ef0b4 100644 --- a/tests/slack_bolt_async/agent/test_async_agent.py +++ b/tests/slack_bolt_async/agent/test_async_agent.py @@ -214,7 +214,7 @@ async def test_set_status_with_loading_messages(self): @pytest.mark.asyncio async def test_set_status_overrides_context_defaults(self): - """Explicit channel/thread_ts override context defaults.""" + """Explicit channel_id/thread_ts override context defaults.""" client = MagicMock(spec=AsyncWebClient) client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() @@ -227,7 +227,7 @@ async def test_set_status_overrides_context_defaults(self): ) await agent.set_status( status="Thinking...", - channel="C999", + channel_id="C999", thread_ts="9999999999.999999", ) @@ -331,7 +331,7 @@ async def test_set_suggested_prompts_with_dict_prompts(self): @pytest.mark.asyncio async def test_set_suggested_prompts_overrides_context_defaults(self): - """Explicit channel/thread_ts override context defaults.""" + """Explicit channel_id/thread_ts override context defaults.""" client = MagicMock(spec=AsyncWebClient) client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() @@ -344,7 +344,7 @@ async def test_set_suggested_prompts_overrides_context_defaults(self): ) await agent.set_suggested_prompts( prompts=["Hello"], - channel="C999", + channel_id="C999", thread_ts="9999999999.999999", ) From bf767eb22fa3dcb2e622e20734247864ac02cbbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Mar 2026 22:16:55 +0000 Subject: [PATCH 190/282] chore(deps): bump actions/stale from 10.1.1 to 10.2.0 (#1448) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/triage-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index cf13d3afc..c29bface2 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -16,7 +16,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: days-before-issue-stale: 30 days-before-issue-close: 10 From 0f3afc22e6697f32c32dc1600e7dfbc0ecf15138 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 08:41:08 -0800 Subject: [PATCH 191/282] chore(deps): bump actions/upload-artifact from 6.0.0 to 7.0.0 (#1449) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 9c9003c92..34025a6fd 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -33,7 +33,7 @@ jobs: scripts/build_pypi_package.sh - name: Persist dist folder - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: release-dist path: dist/ From 5a153e689fc1ac2a1bf67a1a9f5ea4cef10e3cce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:58:33 +0000 Subject: [PATCH 192/282] chore(deps): bump actions/download-artifact from 7.0.0 to 8.0.0 (#1450) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 34025a6fd..7ec974574 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -52,7 +52,7 @@ jobs: steps: - name: Retrieve dist folder - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: release-dist path: dist/ @@ -76,7 +76,7 @@ jobs: steps: - name: Retrieve dist folder - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: release-dist path: dist/ From 72a90d242086d7e77abd0f1be076d6d64f74fa82 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 6 Mar 2026 10:34:16 -0800 Subject: [PATCH 193/282] chore(claude): add claude code support for maintainers (#1445) Co-authored-by: William Bergamin Co-authored-by: William Bergamin --- .claude/.gitignore | 4 + .claude/CLAUDE.md | 1 + .claude/settings.json | 34 +++++ .gitignore | 3 - AGENTS.md | 202 +++++++++++++++++++++++++++ scripts/install.sh | 22 +++ scripts/install_all_and_run_tests.sh | 30 ++-- scripts/run_tests.sh | 10 +- slack_bolt/warning/__init__.py | 1 + 9 files changed, 275 insertions(+), 32 deletions(-) create mode 100644 .claude/.gitignore create mode 100644 .claude/CLAUDE.md create mode 100644 .claude/settings.json create mode 100644 AGENTS.md create mode 100755 scripts/install.sh diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 000000000..3a2f7f6a1 --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1,4 @@ +CLAUDE.local.md +settings.local.json +worktrees/ +plans/ diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 000000000..dba71e970 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +@../AGENTS.md diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..705fd286c --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,34 @@ +{ + "permissions": { + "allow": [ + "Bash(./scripts/build_pypi_package.sh:*)", + "Bash(./scripts/format.sh:*)", + "Bash(./scripts/install_all_and_run_tests.sh:*)", + "Bash(./scripts/lint.sh:*)", + "Bash(./scripts/run_mypy.sh:*)", + "Bash(./scripts/run_tests.sh:*)", + "Bash(./scripts/install.sh:*)", + "Bash(echo $VIRTUAL_ENV)", + "Bash(gh issue view:*)", + "Bash(gh label list:*)", + "Bash(gh pr checks:*)", + "Bash(gh pr diff:*)", + "Bash(gh pr list:*)", + "Bash(gh pr status:*)", + "Bash(gh pr update-branch:*)", + "Bash(gh pr view:*)", + "Bash(gh search code:*)", + "Bash(git diff:*)", + "Bash(git grep:*)", + "Bash(git log:*)", + "Bash(git show:*)", + "Bash(git status:*)", + "Bash(grep:*)", + "Bash(ls:*)", + "Bash(tree:*)", + "WebFetch(domain:github.com)", + "WebFetch(domain:docs.slack.dev)", + "WebFetch(domain:raw.githubusercontent.com)" + ] + } +} diff --git a/.gitignore b/.gitignore index b28dfa9ed..2549060e7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,9 +17,6 @@ venv/ .venv* .env/ -# claude -.claude/*.local.json - # codecov / coverage .coverage cov_* diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..537cabfcf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,202 @@ +# AGENTS.md - bolt-python + +## Project Overview + +Slack Bolt for Python -- a framework for building Slack apps in Python. + +- **Foundation:** Built on top of `slack_sdk` (see `pyproject.toml` constraints). +- **Execution Models:** Supports both synchronous (`App`) and asynchronous (`AsyncApp` using `asyncio`) execution. Async mode requires `aiohttp` as an additional dependency. +- **Framework Adapters:** Features built-in adapters for web frameworks (Flask, FastAPI, Django, Tornado, Pyramid, and many more) and serverless environments (AWS Lambda, Google Cloud Functions). +- **Python Version:** Requires Python 3.7+ as defined in `pyproject.toml`. + +- **Repository**: +- **Documentation**: +- **PyPI**: +- **Current version**: defined in `slack_bolt/version.py` (referenced by `pyproject.toml` via `[tool.setuptools.dynamic]`) + +## Environment Setup + +A python virtual environment (`venv`) should be activated before running any commands. + +```bash +# Create a venv (first time only) +python -m venv .venv + +# Activate +source .venv/bin/activate + +# Install all dependencies +./scripts/install.sh +``` + +You can verify the venv is active by checking `echo $VIRTUAL_ENV`. If tools like `black`, `flake8`, `mypy` or `pytest` are not found, ask the user to activate the venv. + +## Common Commands + +### Testing + +Always use the project scripts instead of calling `pytest` directly: + +```bash +# Install all dependencies and run all tests (formats, lints, tests, typechecks) +./scripts/install_all_and_run_tests.sh + +# Run a single test file +./scripts/run_tests.sh tests/scenario_tests/test_app.py + +# Run a single test function +./scripts/run_tests.sh tests/scenario_tests/test_app.py::TestApp::test_name +``` + +### Formatting, Linting, Type Checking + +```bash +# Format (black, line-length=125) +./scripts/format.sh --no-install + +# Lint (flake8, line-length=125, ignores: F841,F821,W503,E402) +./scripts/lint.sh --no-install + +# Type check (mypy) +./scripts/run_mypy.sh --no-install +``` + +## Architecture + +### Request Processing Pipeline + +Incoming requests flow through a middleware chain before reaching listeners: + +1. **SSL Check** -> **Request Verification** (signature) -> **URL Verification** -> **Authorization** (token injection) -> **Ignoring Self Events** -> Custom middleware +2. **Listener Matching** -- `ListenerMatcher` implementations check if a listener should handle the request +3. **Listener Execution** -- listener-specific middleware runs, then `ack()` is called, then the handler executes + +For FaaS environments (`process_before_response=True`), long-running handlers execute as "lazy listeners" in a thread pool after the ack response is returned. + +### Core Abstractions + +- **`App` / `AsyncApp`** (`slack_bolt/app/`) -- Central class. Registers listeners via decorators (`@app.event()`, `@app.action()`, `@app.command()`, `@app.message()`, `@app.view()`, `@app.shortcut()`, `@app.options()`, `@app.function()`). Dispatches incoming requests through middleware to matching listeners. +- **`Middleware`** (`slack_bolt/middleware/`) -- Abstract base with `process(req, resp, next)`. Built-in: authorization, request verification, SSL check, URL verification, assistant, self-event ignoring. +- **`Listener`** (`slack_bolt/listener/`) -- Has matchers, middleware, and an ack/handler function. `CustomListener` is the main implementation. +- **`ListenerMatcher`** (`slack_bolt/listener_matcher/`) -- Determines if a listener handles a given request. Built-in matchers for events, actions, commands, messages (regex), shortcuts, views, options, functions. +- **`BoltContext`** (`slack_bolt/context/`) -- Dict-like object passed to listeners with `client`, `say()`, `ack()`, `respond()`, `complete()`, `fail()`, plus event metadata (`user_id`, `channel_id`, `team_id`, etc.). +- **`BoltRequest` / `BoltResponse`** (`slack_bolt/request/`, `slack_bolt/response/`) -- Request/response wrappers. Request has `mode` of "http" or "socket_mode". + +### Kwargs Injection + +Listeners receive arguments by parameter name. The framework inspects function signatures and injects matching args: `body`, `event`, `action`, `command`, `payload`, `context`, `client`, `ack`, `say`, `respond`, `logger`, `complete`, `fail`, `agent`, etc. Defined in `slack_bolt/kwargs_injection/args.py`. + +### Adapter System + +Each adapter in `slack_bolt/adapter/` converts between a web framework's request/response types and `BoltRequest`/`BoltResponse`. Adapters exist for: Flask, FastAPI, Django, Starlette, Sanic, Bottle, Tornado, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, Socket Mode, WSGI, ASGI, and more. + +### Sync/Async Mirroring Pattern + +**This is the most important pattern in this codebase.** Almost every module has both a sync and async variant. When you modify one, you almost always must modify the other. + +**File naming convention:** Async files use the `async_` prefix alongside their sync counterpart: + +```text +slack_bolt/middleware/custom_middleware.py # sync +slack_bolt/middleware/async_custom_middleware.py # async + +slack_bolt/context/say/say.py # sync +slack_bolt/context/say/async_say.py # async + +slack_bolt/listener/custom_listener.py # sync +slack_bolt/listener/async_listener.py # async + +slack_bolt/adapter/fastapi/async_handler.py # async-only (no sync FastAPI adapter) +slack_bolt/adapter/flask/handler.py # sync-only (no async Flask adapter) +``` + +**Which modules come in sync/async pairs:** + +- `slack_bolt/app/` -- `app.py` / `async_app.py` +- `slack_bolt/middleware/` -- every middleware has an `async_` counterpart +- `slack_bolt/listener/` -- `listener.py` / `async_listener.py`, plus error/completion/start handlers +- `slack_bolt/listener_matcher/` -- `builtins.py` / `async_builtins.py` +- `slack_bolt/context/` -- each subdirectory (e.g., `say/`, `ack/`, `respond/`) has `async_` variants +- `slack_bolt/kwargs_injection/` -- `args.py` / `async_args.py`, `utils.py` / `async_utils.py` + +**Adapters are an exception:** Most adapters are sync-only or async-only depending on the framework. Async-native frameworks (FastAPI, Starlette, Sanic, Tornado, ASGI, Socket Mode) have `async_handler.py`. Sync-only frameworks (Flask, Django, Bottle, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, WSGI) have `handler.py`. + +### AI Agents & Assistants + +`BoltAgent` (`slack_bolt/agent/`) provides `chat_stream()`, `set_status()`, and `set_suggested_prompts()` for AI-powered agents. `Assistant` middleware (`slack_bolt/middleware/assistant/`) handles assistant thread events. + +## Key Development Patterns + +### Adding or Modifying Middleware + +1. Implement the sync version in `slack_bolt/middleware/` (subclass `Middleware`, implement `process()`) +2. Implement the async version with `async_` prefix (subclass `AsyncMiddleware`, implement `async_process()`) +3. Export built-in middleware from `slack_bolt/middleware/__init__.py` (sync) and `async_builtins.py` (async) + +### Adding a Context Utility + +Each context utility lives in its own subdirectory under `slack_bolt/context/`: + +```text +slack_bolt/context/my_util/ + __init__.py + my_util.py # sync implementation + async_my_util.py # async implementation + internals.py # shared logic (optional) +``` + +Then wire it into `BoltContext` (`slack_bolt/context/context.py`) and `AsyncBoltContext` (`slack_bolt/context/async_context.py`). + +### Adding a New Adapter + +1. Create `slack_bolt/adapter//` +2. Add `__init__.py` and `handler.py` (or `async_handler.py` for async frameworks) +3. The handler converts the framework's request to `BoltRequest`, calls `app.dispatch()`, and converts `BoltResponse` back +4. Add the framework to `requirements/adapter.txt` with version constraints +5. Add adapter tests in `tests/adapter_tests/` (or `tests/adapter_tests_async/`) + +### Adding a Kwargs-Injectable Argument + +1. Add the new arg to `slack_bolt/kwargs_injection/args.py` and `async_args.py` +2. Update the `Args` class with the new property +3. Populate the arg in the appropriate context or listener setup code + +## Dependencies + +The core package has a **single required runtime dependency**: `slack_sdk` (defined in `pyproject.toml`). Do not add runtime dependencies. + +**`requirements/` directory structure:** + +- `async.txt` -- async runtime deps (`aiohttp`, `websockets`) +- `adapter.txt` -- all framework adapter deps (Flask, Django, FastAPI, etc.) +- `testing.txt` -- test runner deps (`pytest`, `pytest-asyncio`, includes `async.txt`) +- `testing_without_asyncio.txt` -- test deps without async (`pytest`, `pytest-cov`) +- `adapter_testing.txt` -- adapter-specific test deps (`moto`, `boddle`, `sanic-testing`) +- `tools.txt` -- dev tools (`mypy`, `flake8`, `black`) + +When adding a new dependency: add it to the appropriate `requirements/*.txt` file with version constraints, never to `pyproject.toml` `dependencies` (unless it's a core runtime dep, which is very rare). + +## Test Organization + +- `tests/scenario_tests/` -- Integration-style tests with realistic Slack payloads +- `tests/slack_bolt/` -- Unit tests mirroring the source structure +- `tests/adapter_tests/` and `tests/adapter_tests_async/` -- Framework adapter tests +- `tests/mock_web_api_server/` -- Mock Slack API server used by tests +- Async test variants use `_async` suffix directories + +**Where to put new tests:** Mirror the source structure. For `slack_bolt/middleware/foo.py`, add tests in `tests/slack_bolt/middleware/test_foo.py`. For async variants, use the `_async` suffix directory or file naming pattern. Adapter tests go in `tests/adapter_tests/` (sync) or `tests/adapter_tests_async/` (async). + +**Mock server:** Many tests use `tests/mock_web_api_server/` to simulate Slack API responses. Look at existing tests for usage patterns rather than making real API calls. + +## Code Style + +- **Black** formatter configured in `pyproject.toml` (line-length=125) +- **Flake8** linter configured in `.flake8` (line-length=125, ignores: F841,F821,W503,E402) +- **MyPy** configured in `pyproject.toml` +- **pytest** configured in `pyproject.toml` + +## GitHub & CI/CD + +- `.github/` -- GitHub-specific configuration and documentation +- `.github/workflows/` -- Continuous integration pipeline definitions that run on GitHub Actions +- `.github/maintainers_guide.md` -- Maintainer workflows and release process diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 000000000..96159c63c --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Installs all dependencies of the project +# ./scripts/install.sh + +script_dir=`dirname $0` +cd ${script_dir}/.. +rm -rf ./slack_bolt.egg-info + +# Update pip to prevent warnings +pip install -U pip + +# The package causes a conflict with moto +pip uninstall python-lambda + +pip install -U -e . +pip install -U -r requirements/testing.txt +pip install -U -r requirements/adapter.txt +pip install -U -r requirements/adapter_testing.txt +pip install -U -r requirements/tools.txt + +# To avoid errors due to the old versions of click forced by Chalice +pip install -U pip click diff --git a/scripts/install_all_and_run_tests.sh b/scripts/install_all_and_run_tests.sh index 2bb9a2050..939e71ffd 100755 --- a/scripts/install_all_and_run_tests.sh +++ b/scripts/install_all_and_run_tests.sh @@ -5,31 +5,19 @@ script_dir=`dirname $0` cd ${script_dir}/.. -rm -rf ./slack_bolt.egg-info -# Update pip to prevent warnings -pip install -U pip +test_target="${1:-tests/}" -# The package causes a conflict with moto -pip uninstall python-lambda +# keep in sync with LATEST_SUPPORTED_PY in .github/workflows/ci-build.yml +LATEST_SUPPORTED_PY="3.14" +current_py=$(python --version | sed -E 's/Python ([0-9]+\.[0-9]+).*/\1/') -test_target="$1" +./scripts/install.sh -pip install -U -e . -pip install -U -r requirements/testing.txt -pip install -U -r requirements/adapter.txt -pip install -U -r requirements/adapter_testing.txt -pip install -U -r requirements/tools.txt -# To avoid errors due to the old versions of click forced by Chalice -pip install -U pip click +./scripts/format.sh --no-install +./scripts/lint.sh --no-install +pytest $test_target -if [[ $test_target != "" ]] -then - ./scripts/format.sh --no-install - pytest $1 -else - ./scripts/format.sh --no-install - ./scripts/lint.sh --no-install - pytest +if [[ "$current_py" == "$LATEST_SUPPORTED_PY" ]]; then ./scripts/run_mypy.sh --no-install fi diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index cdac3c71c..d4dc767e3 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -6,13 +6,7 @@ script_dir=`dirname $0` cd ${script_dir}/.. -test_target="$1" +test_target="${1:-tests/}" ./scripts/format.sh --no-install - -if [[ $test_target != "" ]] -then - pytest -vv $1 -else - pytest -fi +pytest -vv $test_target diff --git a/slack_bolt/warning/__init__.py b/slack_bolt/warning/__init__.py index df71b812f..4991f4cd9 100644 --- a/slack_bolt/warning/__init__.py +++ b/slack_bolt/warning/__init__.py @@ -3,4 +3,5 @@ class ExperimentalWarning(FutureWarning): """Warning for features that are still in experimental phase.""" + pass From 0fc53805a262c21d80057b41b07552f1d1ac072d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:19:30 -0700 Subject: [PATCH 194/282] chore(deps): bump black from 25.1.0 to 26.3.1 in /requirements (#1457) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tools.txt b/requirements/tools.txt index 7609eb52e..dd13bd614 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,3 +1,3 @@ mypy==1.19.1 flake8==7.3.0 -black==25.1.0 +black==26.3.1 From 4d15431ca824963017e6e8b5e4a5b3cc90232d53 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 12 Mar 2026 14:55:40 -0700 Subject: [PATCH 195/282] chore: improve AGENTS.md (#1458) --- AGENTS.md | 156 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 56 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 537cabfcf..57f2fa588 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,8 @@ Slack Bolt for Python -- a framework for building Slack apps in Python. ## Environment Setup +You can verify the venv is active by checking `echo $VIRTUAL_ENV`. If tools like `black`, `flake8`, `mypy` or `pytest` are not found, ask the user to activate the venv. + A python virtual environment (`venv`) should be activated before running any commands. ```bash @@ -29,18 +31,30 @@ source .venv/bin/activate ./scripts/install.sh ``` -You can verify the venv is active by checking `echo $VIRTUAL_ENV`. If tools like `black`, `flake8`, `mypy` or `pytest` are not found, ask the user to activate the venv. - ## Common Commands -### Testing +### Pre-submission Checklist -Always use the project scripts instead of calling `pytest` directly: +Before considering any work complete, you MUST run these commands in order and confirm they all pass: + +```bash +./scripts/format.sh --no-install # 1. Format +./scripts/lint.sh --no-install # 2. Lint +./scripts/run_tests.sh # 3. Run relevant tests (see Testing below) +./scripts/run_mypy.sh --no-install # 4. Type check +``` + +To run everything at once (installs deps + formats + lints + tests + typechecks): ```bash -# Install all dependencies and run all tests (formats, lints, tests, typechecks) ./scripts/install_all_and_run_tests.sh +``` +### Testing + +Always use the project scripts instead of calling `pytest` directly: + +```bash # Run a single test file ./scripts/run_tests.sh tests/scenario_tests/test_app.py @@ -51,16 +65,70 @@ Always use the project scripts instead of calling `pytest` directly: ### Formatting, Linting, Type Checking ```bash -# Format (black, line-length=125) +# Format -- Black, configured in pyproject.toml ./scripts/format.sh --no-install -# Lint (flake8, line-length=125, ignores: F841,F821,W503,E402) +# Lint -- Flake8, configured in .flake8 ./scripts/lint.sh --no-install -# Type check (mypy) +# Type check -- mypy, configured in pyproject.toml ./scripts/run_mypy.sh --no-install ``` +## Critical Conventions + +### Sync/Async Mirroring Rule + +**When modifying any sync module, you MUST also update the corresponding async module (and vice versa).** This is the most important convention in this codebase. + +Almost every module has both a sync and async variant. Async files use the `async_` prefix alongside their sync counterpart: + +```text +slack_bolt/middleware/custom_middleware.py # sync +slack_bolt/middleware/async_custom_middleware.py # async + +slack_bolt/context/say/say.py # sync +slack_bolt/context/say/async_say.py # async + +slack_bolt/listener/custom_listener.py # sync +slack_bolt/listener/async_listener.py # async +``` + +**Modules that come in sync/async pairs:** + +- `slack_bolt/app/` -- `app.py` / `async_app.py` +- `slack_bolt/middleware/` -- every middleware has an `async_` counterpart +- `slack_bolt/listener/` -- `listener.py` / `async_listener.py`, plus error/completion/start handlers +- `slack_bolt/listener_matcher/` -- `builtins.py` / `async_builtins.py` +- `slack_bolt/context/` -- each subdirectory (e.g., `say/`, `ack/`, `respond/`) has `async_` variants +- `slack_bolt/kwargs_injection/` -- `args.py` / `async_args.py`, `utils.py` / `async_utils.py` + +**Adapters are an exception:** Most adapters are sync-only or async-only depending on the framework. Async-native frameworks (FastAPI, Starlette, Sanic, Tornado, ASGI, Socket Mode) have `async_handler.py`. Sync-only frameworks (Flask, Django, Bottle, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, WSGI) have `handler.py`. + +### Prefer the Middleware Pattern + +Middleware is the project's preferred approach for cross-cutting concerns. Before adding logic to individual listeners or utility functions, consider whether it belongs as a built-in middleware in the framework. + +**When to add built-in middleware:** + +- Cross-cutting concerns that apply to many or all requests (logging, metrics, observability) +- Request validation, transformation, or enrichment +- Authorization extensions beyond the built-in `SingleTeamAuthorization`/`MultiTeamsAuthorization` +- Feature-level request handling (the `Assistant` middleware in `slack_bolt/middleware/assistant/assistant.py` is the canonical example -- it intercepts assistant thread events and dispatches them to registered sub-listeners) + +**How to add built-in middleware:** + +1. Subclass `Middleware` (sync) and implement `process(self, *, req, resp, next)`. Call `next()` to continue the chain. +2. Subclass `AsyncMiddleware` (async) and implement `async_process(self, *, req, resp, next)`. Call `await next()` to continue. +3. Export from `slack_bolt/middleware/__init__.py` (sync) and `slack_bolt/middleware/async_builtins.py` (async). +4. Register the middleware in `App.__init__()` (`slack_bolt/app/app.py`) and `AsyncApp.__init__()` (`slack_bolt/app/async_app.py`) where the default middleware chain is assembled. + +**Canonical example:** `AttachingFunctionToken` (`slack_bolt/middleware/attaching_function_token/`) is a good small middleware to follow -- it has a clean sync/async pair, a focused `process()` method, and is properly exported and registered in the app's middleware chain. + +### Single Runtime Dependency Rule + +The core package depends ONLY on `slack_sdk` (defined in `pyproject.toml`). Never add runtime dependencies to `pyproject.toml`. Additional dependencies go in the appropriate `requirements/*.txt` file. + ## Architecture ### Request Processing Pipeline @@ -90,49 +158,12 @@ Listeners receive arguments by parameter name. The framework inspects function s Each adapter in `slack_bolt/adapter/` converts between a web framework's request/response types and `BoltRequest`/`BoltResponse`. Adapters exist for: Flask, FastAPI, Django, Starlette, Sanic, Bottle, Tornado, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, Socket Mode, WSGI, ASGI, and more. -### Sync/Async Mirroring Pattern - -**This is the most important pattern in this codebase.** Almost every module has both a sync and async variant. When you modify one, you almost always must modify the other. - -**File naming convention:** Async files use the `async_` prefix alongside their sync counterpart: - -```text -slack_bolt/middleware/custom_middleware.py # sync -slack_bolt/middleware/async_custom_middleware.py # async - -slack_bolt/context/say/say.py # sync -slack_bolt/context/say/async_say.py # async - -slack_bolt/listener/custom_listener.py # sync -slack_bolt/listener/async_listener.py # async - -slack_bolt/adapter/fastapi/async_handler.py # async-only (no sync FastAPI adapter) -slack_bolt/adapter/flask/handler.py # sync-only (no async Flask adapter) -``` - -**Which modules come in sync/async pairs:** - -- `slack_bolt/app/` -- `app.py` / `async_app.py` -- `slack_bolt/middleware/` -- every middleware has an `async_` counterpart -- `slack_bolt/listener/` -- `listener.py` / `async_listener.py`, plus error/completion/start handlers -- `slack_bolt/listener_matcher/` -- `builtins.py` / `async_builtins.py` -- `slack_bolt/context/` -- each subdirectory (e.g., `say/`, `ack/`, `respond/`) has `async_` variants -- `slack_bolt/kwargs_injection/` -- `args.py` / `async_args.py`, `utils.py` / `async_utils.py` - -**Adapters are an exception:** Most adapters are sync-only or async-only depending on the framework. Async-native frameworks (FastAPI, Starlette, Sanic, Tornado, ASGI, Socket Mode) have `async_handler.py`. Sync-only frameworks (Flask, Django, Bottle, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, WSGI) have `handler.py`. - ### AI Agents & Assistants `BoltAgent` (`slack_bolt/agent/`) provides `chat_stream()`, `set_status()`, and `set_suggested_prompts()` for AI-powered agents. `Assistant` middleware (`slack_bolt/middleware/assistant/`) handles assistant thread events. ## Key Development Patterns -### Adding or Modifying Middleware - -1. Implement the sync version in `slack_bolt/middleware/` (subclass `Middleware`, implement `process()`) -2. Implement the async version with `async_` prefix (subclass `AsyncMiddleware`, implement `async_process()`) -3. Export built-in middleware from `slack_bolt/middleware/__init__.py` (sync) and `async_builtins.py` (async) - ### Adding a Context Utility Each context utility lives in its own subdirectory under `slack_bolt/context/`: @@ -153,7 +184,7 @@ Then wire it into `BoltContext` (`slack_bolt/context/context.py`) and `AsyncBolt 2. Add `__init__.py` and `handler.py` (or `async_handler.py` for async frameworks) 3. The handler converts the framework's request to `BoltRequest`, calls `app.dispatch()`, and converts `BoltResponse` back 4. Add the framework to `requirements/adapter.txt` with version constraints -5. Add adapter tests in `tests/adapter_tests/` (or `tests/adapter_tests_async/`) +5. Add adapter tests in `tests/adapter_tests/` (sync) or `tests/adapter_tests_async/` (async) ### Adding a Kwargs-Injectable Argument @@ -161,6 +192,13 @@ Then wire it into `BoltContext` (`slack_bolt/context/context.py`) and `AsyncBolt 2. Update the `Args` class with the new property 3. Populate the arg in the appropriate context or listener setup code +## Security Considerations + +- **Request Verification:** The built-in `RequestVerification` middleware validates `x-slack-signature` and `x-slack-request-timestamp` on every incoming HTTP request. Never disable this in production. It is automatically skipped for `socket_mode` requests. +- **Tokens & Secrets:** `SLACK_SIGNING_SECRET` and `SLACK_BOT_TOKEN` must come from environment variables. Never hardcode or commit secrets. +- **Authorization Middleware:** `SingleTeamAuthorization` and `MultiTeamsAuthorization` verify tokens and inject an authorized `WebClient` into the context. Do not bypass these. +- **Tests:** Always use mock servers (`tests/mock_web_api_server/`) and dummy values. Never use real tokens in tests. + ## Dependencies The core package has a **single required runtime dependency**: `slack_sdk` (defined in `pyproject.toml`). Do not add runtime dependencies. @@ -176,7 +214,9 @@ The core package has a **single required runtime dependency**: `slack_sdk` (defi When adding a new dependency: add it to the appropriate `requirements/*.txt` file with version constraints, never to `pyproject.toml` `dependencies` (unless it's a core runtime dep, which is very rare). -## Test Organization +## Test Organization and CI + +### Directory Structure - `tests/scenario_tests/` -- Integration-style tests with realistic Slack payloads - `tests/slack_bolt/` -- Unit tests mirroring the source structure @@ -188,15 +228,19 @@ When adding a new dependency: add it to the appropriate `requirements/*.txt` fil **Mock server:** Many tests use `tests/mock_web_api_server/` to simulate Slack API responses. Look at existing tests for usage patterns rather than making real API calls. -## Code Style +### CI Pipeline + +GitHub Actions (`.github/workflows/ci-build.yml`) runs on every push to `main` and every PR: -- **Black** formatter configured in `pyproject.toml` (line-length=125) -- **Flake8** linter configured in `.flake8` (line-length=125, ignores: F841,F821,W503,E402) -- **MyPy** configured in `pyproject.toml` -- **pytest** configured in `pyproject.toml` +- **Lint** -- `./scripts/lint.sh` on latest Python +- **Typecheck** -- `./scripts/run_mypy.sh` on latest Python +- **Unit tests** -- full test suite across Python 3.7--3.14 matrix +- **Code coverage** -- uploaded to Codecov -## GitHub & CI/CD +## PR and Commit Guidelines -- `.github/` -- GitHub-specific configuration and documentation -- `.github/workflows/` -- Continuous integration pipeline definitions that run on GitHub Actions -- `.github/maintainers_guide.md` -- Maintainer workflows and release process +- PRs target the `main` branch +- You MUST run `./scripts/install_all_and_run_tests.sh` before submitting +- PR template (`.github/pull_request_template.md`) requires: Summary, Testing steps, Category checkboxes (`App`, `AsyncApp`, Adapters, Docs, Others) +- Requirements: CLA signed, test suite passes, code review approval +- Commits should be atomic with descriptive messages. Reference related issue numbers. From 898e0b8c987de9df42c89640e0bcbf4acd7a2e56 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 16 Mar 2026 11:35:03 -0700 Subject: [PATCH 196/282] chore: format project to latest fomatter version (#1460) --- slack_bolt/app/app.py | 4 +- slack_bolt/app/async_app.py | 6 +- slack_bolt/middleware/assistant/assistant.py | 18 ++-- .../middleware/assistant/async_assistant.py | 26 ++--- slack_bolt/request/payload_utils.py | 1 - tests/scenario_tests/test_view_submission.py | 1 - .../test_view_submission.py | 1 - .../logger/test_unmatched_suggestions.py | 98 ++++++------------- .../logger/test_unmatched_suggestions.py | 98 ++++++------------- 9 files changed, 83 insertions(+), 170 deletions(-) diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index 5a7f32917..fcf5bb788 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -1401,7 +1401,7 @@ def _init_context(self, req: BoltRequest): # For AI Agents & Assistants if is_assistant_event(req.body): assistant = AssistantUtilities( - payload=to_event(req.body), # type:ignore[arg-type] + payload=to_event(req.body), # type: ignore[arg-type] context=req.context, thread_context_store=self._assistant_thread_context_store, ) @@ -1457,7 +1457,7 @@ def _register_listener( CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, # type:ignore[arg-type] + lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 39f3c3c0e..62c491084 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -616,7 +616,7 @@ async def async_middleware_next(): self._framework_logger.debug(debug_checking_listener(listener_name)) if await listener.async_matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - (middleware_resp, next_was_not_called) = await listener.run_async_middleware( + middleware_resp, next_was_not_called = await listener.run_async_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: @@ -1434,7 +1434,7 @@ def _init_context(self, req: AsyncBoltRequest): # For AI Agents & Assistants if is_assistant_event(req.body): assistant = AsyncAssistantUtilities( - payload=to_event(req.body), # type:ignore[arg-type] + payload=to_event(req.body), # type: ignore[arg-type] context=req.context, thread_context_store=self._assistant_thread_context_store, ) @@ -1495,7 +1495,7 @@ def _register_listener( AsyncCustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, # type:ignore[arg-type] + lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, diff --git a/slack_bolt/middleware/assistant/assistant.py b/slack_bolt/middleware/assistant/assistant.py index beac71bca..d61386105 100644 --- a/slack_bolt/middleware/assistant/assistant.py +++ b/slack_bolt/middleware/assistant/assistant.py @@ -67,7 +67,7 @@ def thread_started( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -106,7 +106,7 @@ def user_message( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -145,7 +145,7 @@ def bot_message( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -184,7 +184,7 @@ def thread_context_changed( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -214,13 +214,13 @@ def _merge_matchers( ): return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + ( custom_matchers or [] - ) # type:ignore[operator] + ) # type: ignore[operator] @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) - def process( # type:ignore[return] + def process( # type: ignore[return] self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse] ) -> Optional[BoltResponse]: if self._thread_context_changed_listeners is None: @@ -255,8 +255,8 @@ def build_listener( middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions @@ -270,7 +270,7 @@ def build_listener( for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) - elif isinstance(matcher, Callable): # type:ignore[arg-type] + elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, diff --git a/slack_bolt/middleware/assistant/async_assistant.py b/slack_bolt/middleware/assistant/async_assistant.py index 2fdd828d7..ae82595a8 100644 --- a/slack_bolt/middleware/assistant/async_assistant.py +++ b/slack_bolt/middleware/assistant/async_assistant.py @@ -63,7 +63,7 @@ def thread_started( func=is_assistant_thread_started_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -72,7 +72,7 @@ def thread_started( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -109,7 +109,7 @@ def user_message( func=is_user_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -118,7 +118,7 @@ def user_message( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -155,7 +155,7 @@ def bot_message( func=is_bot_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -164,7 +164,7 @@ def bot_message( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -201,7 +201,7 @@ def thread_context_changed( func=is_assistant_thread_context_changed_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -210,7 +210,7 @@ def thread_context_changed( self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -238,14 +238,14 @@ def _merge_matchers( primary_matcher: Union[Callable[..., bool], AsyncListenerMatcher], custom_matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]], ): - return [primary_matcher] + (custom_matchers or []) # type:ignore[operator] + return [primary_matcher] + (custom_matchers or []) # type: ignore[operator] @staticmethod async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict): new_context: dict = payload["assistant_thread"]["context"] await save_thread_context(new_context) - async def async_process( # type:ignore[return] + async def async_process( # type: ignore[return] self, *, req: AsyncBoltRequest, @@ -284,8 +284,8 @@ def build_listener( middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[Logger] = None, ) -> AsyncListener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, AsyncListener): return listener_or_functions @@ -302,7 +302,7 @@ def build_listener( else: listener_matchers.append( build_listener_matcher( - func=matcher, # type:ignore[arg-type] + func=matcher, # type: ignore[arg-type] asyncio=True, base_logger=base_logger, ) diff --git a/slack_bolt/request/payload_utils.py b/slack_bolt/request/payload_utils.py index c1016c65d..1ebf70d4f 100644 --- a/slack_bolt/request/payload_utils.py +++ b/slack_bolt/request/payload_utils.py @@ -1,6 +1,5 @@ from typing import Dict, Any, Optional - # ------------------------------------------ # Public Utilities # ------------------------------------------ diff --git a/tests/scenario_tests/test_view_submission.py b/tests/scenario_tests/test_view_submission.py index b0eb58212..0f5b23f85 100644 --- a/tests/scenario_tests/test_view_submission.py +++ b/tests/scenario_tests/test_view_submission.py @@ -14,7 +14,6 @@ ) from tests.utils import remove_os_env_temporarily, restore_os_env - body = { "type": "view_submission", "team": { diff --git a/tests/scenario_tests_async/test_view_submission.py b/tests/scenario_tests_async/test_view_submission.py index 49a6e8fc5..6511243fa 100644 --- a/tests/scenario_tests_async/test_view_submission.py +++ b/tests/scenario_tests_async/test_view_submission.py @@ -15,7 +15,6 @@ ) from tests.utils import remove_os_env_temporarily, restore_os_env - body = { "type": "view_submission", "team": { diff --git a/tests/slack_bolt/logger/test_unmatched_suggestions.py b/tests/slack_bolt/logger/test_unmatched_suggestions.py index 2c0c82b99..b470fa061 100644 --- a/tests/slack_bolt/logger/test_unmatched_suggestions.py +++ b/tests/slack_bolt/logger/test_unmatched_suggestions.py @@ -22,8 +22,7 @@ def test_block_actions(self): "block_id": "b", "action_id": "action-id-value", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -31,9 +30,7 @@ def test_block_actions(self): def handle_some_action(ack, body, logger): ack() logger.info(body) -""" - == message - ) +""" == message def test_attachment_actions(self): req: BoltRequest = BoltRequest(body=attachment_actions, mode="socket_mode") @@ -49,8 +46,7 @@ def test_attachment_actions(self): } ], } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -58,9 +54,7 @@ def test_attachment_actions(self): def handle_some_action(ack, body, logger): ack() logger.info(body) -""" - == message - ) +""" == message def test_app_mention_event(self): req: BoltRequest = BoltRequest(body=app_mention_event, mode="socket_mode") @@ -69,17 +63,14 @@ def test_app_mention_event(self): "event": {"type": "app_mention"}, } message = warning_unhandled_request(req) - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @app.event("app_mention") def handle_app_mention_events(body, logger): logger.info(body) -""" - == message - ) +""" == message def test_function_event(self): req: BoltRequest = BoltRequest(body=function_event, mode="socket_mode") @@ -88,8 +79,7 @@ def test_function_event(self): "event": {"type": "function_executed"}, } message = warning_unhandled_request(req) - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -104,9 +94,7 @@ def handle_some_function(ack, body, complete, fail, logger): except Exception as e: error = f"Failed to handle a function request (error: {{e}})" fail(error=error) -""" - == message - ) +""" == message def test_commands(self): req: BoltRequest = BoltRequest(body=slash_command, mode="socket_mode") @@ -115,8 +103,7 @@ def test_commands(self): "type": None, "command": "/start-conv", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -124,9 +111,7 @@ def test_commands(self): def handle_some_command(ack, body, logger): ack() logger.info(body) -""" - == message - ) +""" == message def test_shortcut(self): req: BoltRequest = BoltRequest(body=global_shortcut, mode="socket_mode") @@ -135,8 +120,7 @@ def test_shortcut(self): "type": "shortcut", "callback_id": "test-shortcut", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -144,9 +128,7 @@ def test_shortcut(self): def handle_shortcuts(ack, body, logger): ack() logger.info(body) -""" - == message - ) +""" == message req: BoltRequest = BoltRequest(body=message_shortcut, mode="socket_mode") message = warning_unhandled_request(req) @@ -154,8 +136,7 @@ def handle_shortcuts(ack, body, logger): "type": "message_action", "callback_id": "test-shortcut", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -163,9 +144,7 @@ def handle_shortcuts(ack, body, logger): def handle_shortcuts(ack, body, logger): ack() logger.info(body) -""" - == message - ) +""" == message def test_view(self): req: BoltRequest = BoltRequest(body=view_submission, mode="socket_mode") @@ -174,8 +153,7 @@ def test_view(self): "type": "view_submission", "view": {"type": "modal", "callback_id": "view-id"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -183,9 +161,7 @@ def test_view(self): def handle_view_submission_events(ack, body, logger): ack() logger.info(body) -""" - == message - ) +""" == message req: BoltRequest = BoltRequest(body=view_closed, mode="socket_mode") message = warning_unhandled_request(req) @@ -193,8 +169,7 @@ def handle_view_submission_events(ack, body, logger): "type": "view_closed", "view": {"type": "modal", "callback_id": "view-id"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -202,9 +177,7 @@ def handle_view_submission_events(ack, body, logger): def handle_view_closed_events(ack, body, logger): ack() logger.info(body) -""" - == message - ) +""" == message def test_block_suggestion(self): req: BoltRequest = BoltRequest(body=block_suggestion, mode="socket_mode") @@ -216,17 +189,14 @@ def test_block_suggestion(self): "action_id": "the-id", "value": "search word", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @app.options("the-id") def handle_some_options(ack): ack(options=[ ... ]) -""" - == message - ) +""" == message def test_dialog_suggestion(self): req: BoltRequest = BoltRequest(body=dialog_suggestion, mode="socket_mode") @@ -236,17 +206,14 @@ def test_dialog_suggestion(self): "callback_id": "the-id", "value": "search keyword", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @app.options({{"type": "dialog_suggestion", "callback_id": "the-id"}}) def handle_some_options(ack): ack(options=[ ... ]) -""" - == message - ) +""" == message def test_step(self): req: BoltRequest = BoltRequest(body=step_edit_payload, mode="socket_mode") @@ -255,8 +222,7 @@ def test_step(self): "type": "workflow_step_edit", "callback_id": "copy_review", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -269,17 +235,14 @@ def test_step(self): ) # Pass Step to set up listeners app.step(ws) -""" - == message - ) +""" == message req: BoltRequest = BoltRequest(body=step_save_payload, mode="socket_mode") message = warning_unhandled_request(req) filtered_body = { "type": "view_submission", "view": {"type": "workflow_step", "callback_id": "copy_review"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -292,17 +255,14 @@ def test_step(self): ) # Pass Step to set up listeners app.step(ws) -""" - == message - ) +""" == message req: BoltRequest = BoltRequest(body=step_execute_payload, mode="socket_mode") message = warning_unhandled_request(req) filtered_body = { "type": "event_callback", "event": {"type": "workflow_step_execute"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -315,9 +275,7 @@ def test_step(self): ) # Pass Step to set up listeners app.step(ws) -""" - == message - ) +""" == message block_actions = { diff --git a/tests/slack_bolt_async/logger/test_unmatched_suggestions.py b/tests/slack_bolt_async/logger/test_unmatched_suggestions.py index 93343c4a2..d8c659892 100644 --- a/tests/slack_bolt_async/logger/test_unmatched_suggestions.py +++ b/tests/slack_bolt_async/logger/test_unmatched_suggestions.py @@ -22,8 +22,7 @@ def test_block_actions(self): "block_id": "b", "action_id": "action-id-value", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -31,9 +30,7 @@ def test_block_actions(self): async def handle_some_action(ack, body, logger): await ack() logger.info(body) -""" - == message - ) +""" == message def test_attachment_actions(self): req: AsyncBoltRequest = AsyncBoltRequest(body=attachment_actions, mode="socket_mode") @@ -49,8 +46,7 @@ def test_attachment_actions(self): } ], } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -58,9 +54,7 @@ def test_attachment_actions(self): async def handle_some_action(ack, body, logger): await ack() logger.info(body) -""" - == message - ) +""" == message def test_app_mention_event(self): req: AsyncBoltRequest = AsyncBoltRequest(body=app_mention_event, mode="socket_mode") @@ -69,17 +63,14 @@ def test_app_mention_event(self): "event": {"type": "app_mention"}, } message = warning_unhandled_request(req) - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @app.event("app_mention") async def handle_app_mention_events(body, logger): logger.info(body) -""" - == message - ) +""" == message def test_function_event(self): req: AsyncBoltRequest = AsyncBoltRequest(body=function_event, mode="socket_mode") @@ -88,8 +79,7 @@ def test_function_event(self): "event": {"type": "function_executed"}, } message = warning_unhandled_request(req) - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -104,9 +94,7 @@ async def handle_some_function(ack, body, complete, fail, logger): except Exception as e: error = f"Failed to handle a function request (error: {{e}})" await fail(error=error) -""" - == message - ) +""" == message def test_commands(self): req: AsyncBoltRequest = AsyncBoltRequest(body=slash_command, mode="socket_mode") @@ -115,8 +103,7 @@ def test_commands(self): "type": None, "command": "/start-conv", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -124,9 +111,7 @@ def test_commands(self): async def handle_some_command(ack, body, logger): await ack() logger.info(body) -""" - == message - ) +""" == message def test_shortcut(self): req: AsyncBoltRequest = AsyncBoltRequest(body=global_shortcut, mode="socket_mode") @@ -135,8 +120,7 @@ def test_shortcut(self): "type": "shortcut", "callback_id": "test-shortcut", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -144,9 +128,7 @@ def test_shortcut(self): async def handle_shortcuts(ack, body, logger): await ack() logger.info(body) -""" - == message - ) +""" == message req: AsyncBoltRequest = AsyncBoltRequest(body=message_shortcut, mode="socket_mode") message = warning_unhandled_request(req) @@ -154,8 +136,7 @@ async def handle_shortcuts(ack, body, logger): "type": "message_action", "callback_id": "test-shortcut", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -163,9 +144,7 @@ async def handle_shortcuts(ack, body, logger): async def handle_shortcuts(ack, body, logger): await ack() logger.info(body) -""" - == message - ) +""" == message def test_view(self): req: AsyncBoltRequest = AsyncBoltRequest(body=view_submission, mode="socket_mode") @@ -174,8 +153,7 @@ def test_view(self): "type": "view_submission", "view": {"type": "modal", "callback_id": "view-id"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -183,9 +161,7 @@ def test_view(self): async def handle_view_submission_events(ack, body, logger): await ack() logger.info(body) -""" - == message - ) +""" == message req: AsyncBoltRequest = AsyncBoltRequest(body=view_closed, mode="socket_mode") message = warning_unhandled_request(req) @@ -193,8 +169,7 @@ async def handle_view_submission_events(ack, body, logger): "type": "view_closed", "view": {"type": "modal", "callback_id": "view-id"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -202,9 +177,7 @@ async def handle_view_submission_events(ack, body, logger): async def handle_view_closed_events(ack, body, logger): await ack() logger.info(body) -""" - == message - ) +""" == message def test_block_suggestion(self): req: AsyncBoltRequest = AsyncBoltRequest(body=block_suggestion, mode="socket_mode") @@ -216,17 +189,14 @@ def test_block_suggestion(self): "action_id": "the-id", "value": "search word", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @app.options("the-id") async def handle_some_options(ack): await ack(options=[ ... ]) -""" - == message - ) +""" == message def test_dialog_suggestion(self): req: AsyncBoltRequest = AsyncBoltRequest(body=dialog_suggestion, mode="socket_mode") @@ -236,17 +206,14 @@ def test_dialog_suggestion(self): "callback_id": "the-id", "value": "search keyword", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @app.options({{"type": "dialog_suggestion", "callback_id": "the-id"}}) async def handle_some_options(ack): await ack(options=[ ... ]) -""" - == message - ) +""" == message def test_step(self): req: AsyncBoltRequest = AsyncBoltRequest(body=step_edit_payload, mode="socket_mode") @@ -255,8 +222,7 @@ def test_step(self): "type": "workflow_step_edit", "callback_id": "copy_review", } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -269,17 +235,14 @@ def test_step(self): ) # Pass Step to set up listeners app.step(ws) -""" - == message - ) +""" == message req: AsyncBoltRequest = AsyncBoltRequest(body=step_save_payload, mode="socket_mode") message = warning_unhandled_request(req) filtered_body = { "type": "view_submission", "view": {"type": "workflow_step", "callback_id": "copy_review"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -292,17 +255,14 @@ def test_step(self): ) # Pass Step to set up listeners app.step(ws) -""" - == message - ) +""" == message req: AsyncBoltRequest = AsyncBoltRequest(body=step_execute_payload, mode="socket_mode") message = warning_unhandled_request(req) filtered_body = { "type": "event_callback", "event": {"type": "workflow_step_execute"}, } - assert ( - f"""Unhandled request ({filtered_body}) + assert f"""Unhandled request ({filtered_body}) --- [Suggestion] You can handle this type of event with the following listener function: @@ -315,9 +275,7 @@ def test_step(self): ) # Pass Step to set up listeners app.step(ws) -""" - == message - ) +""" == message block_actions = { From f0db283064225c2247a32bf9febb28efbbe3a498 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 17 Mar 2026 06:21:14 -0700 Subject: [PATCH 197/282] chore: improve testing around assistant utilities (#1461) --- tests/scenario_tests/test_events_assistant.py | 138 +++++++-- ...est_events_assistant_without_middleware.py | 249 ++++++++++++++++ .../test_events_assistant.py | 152 ++++++++-- ...est_events_assistant_without_middleware.py | 268 ++++++++++++++++++ 4 files changed, 750 insertions(+), 57 deletions(-) create mode 100644 tests/scenario_tests/test_events_assistant_without_middleware.py create mode 100644 tests/scenario_tests_async/test_events_assistant_without_middleware.py diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index 07f7ede53..3372380fd 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -1,4 +1,4 @@ -from time import sleep +import time from slack_sdk.web import WebClient @@ -10,6 +10,13 @@ from tests.utils import remove_os_env_temporarily, restore_os_env +def assert_target_called(called: dict, timeout: float = 0.5): + deadline = time.time() + timeout + while called["value"] is not True and time.time() < deadline: + time.sleep(0.1) + assert called["value"] is True + + class TestEventsAssistant: valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" @@ -26,81 +33,156 @@ def teardown_method(self): cleanup_mock_web_api_server(self) restore_os_env(self.old_os_env) - def test_assistant_threads(self): + def test_thread_started(self): app = App(client=self.web_client) assistant = Assistant() - - state = {"called": False} - - def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False + called = {"value": False} @assistant.thread_started - def start_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts, context: BoltContext): + def start_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts, set_status: SetStatus, context: BoltContext): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" + assert set_status.thread_ts == context.thread_ts + assert say.thread_ts == context.thread_ts say("Hi, how can I help you today?") set_suggested_prompts(prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}]) set_suggested_prompts( prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}], title="foo" ) - state["called"] = True + called["value"] = True + + app.assistant(assistant) + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_thread_context_changed(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} @assistant.thread_context_changed def handle_thread_context_changed(context: BoltContext): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" - state["called"] = True + called["value"] = True + + app.assistant(assistant) + + request = BoltRequest(body=thread_context_changed_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_user_message(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} @assistant.user_message def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts try: set_status("is typing...") say("Here you are!") - state["called"] = True + called["value"] = True except Exception as e: - say(f"Oops, something went wrong (error: {e}") + say(f"Oops, something went wrong (error: {e})") app.assistant(assistant) - request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + request = BoltRequest(body=user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called() + assert_target_called(called) - request = BoltRequest(body=thread_context_changed_event_body, mode="socket_mode") - response = app.dispatch(request) - assert response.status == 200 - assert_target_called() + def test_user_message_with_assistant_thread(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} - request = BoltRequest(body=user_message_event_body, mode="socket_mode") - response = app.dispatch(request) - assert response.status == 200 - assert_target_called() + @assistant.user_message + def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + try: + set_status("is typing...") + say("Here you are!") + called["value"] = True + except Exception as e: + say(f"Oops, something went wrong (error: {e})") + + app.assistant(assistant) request = BoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called() + assert_target_called(called) + + def test_message_changed(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} + + @assistant.user_message + def handle_user_message(): + called["value"] = True + + @assistant.bot_message + def handle_bot_message(): + called["value"] = True + + app.assistant(assistant) request = BoltRequest(body=message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 + assert called["value"] is False + + def test_channel_user_message_ignored(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} + + @assistant.user_message + def handle_user_message(): + called["value"] = True + + @assistant.bot_message + def handle_bot_message(): + called["value"] = True + + app.assistant(assistant) request = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 404 + assert called["value"] is False + + def test_channel_message_changed_ignored(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} + + @assistant.user_message + def handle_user_message(): + called["value"] = True + + @assistant.bot_message + def handle_bot_message(): + called["value"] = True + + app.assistant(assistant) request = BoltRequest(body=channel_message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 404 + assert called["value"] is False def build_payload(event: dict) -> dict: diff --git a/tests/scenario_tests/test_events_assistant_without_middleware.py b/tests/scenario_tests/test_events_assistant_without_middleware.py new file mode 100644 index 000000000..5307aa4c6 --- /dev/null +++ b/tests/scenario_tests/test_events_assistant_without_middleware.py @@ -0,0 +1,249 @@ +from slack_sdk.web import WebClient + +from slack_bolt import App, BoltRequest, Say, SetStatus, SetTitle, SaveThreadContext, BoltContext +from slack_bolt.context.get_thread_context.get_thread_context import GetThreadContext +from slack_bolt.context.set_suggested_prompts.set_suggested_prompts import SetSuggestedPrompts +from tests.mock_web_api_server import ( + setup_mock_web_api_server, + cleanup_mock_web_api_server, +) +from tests.scenario_tests.test_events_assistant import ( + assert_target_called, + channel_message_changed_event_body, + channel_user_message_event_body, + message_changed_event_body, + thread_context_changed_event_body, + thread_started_event_body, + user_message_event_body, + user_message_event_body_with_assistant_thread, +) +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestEventsAssistantWithoutMiddleware: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = WebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + def setup_method(self): + self.old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + restore_os_env(self.old_os_env) + + def test_thread_started(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("assistant_thread_started") + def handle_assistant_thread_started( + say: Say, + set_status: SetStatus, + set_title: SetTitle, + set_suggested_prompts: SetSuggestedPrompts, + get_thread_context: GetThreadContext, + save_thread_context: SaveThreadContext, + context: BoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + say("Hi, how can I help you today?") + set_suggested_prompts(prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}]) + called["value"] = True + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_thread_context_changed(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("assistant_thread_context_changed") + def handle_assistant_thread_context_changed( + say: Say, + set_status: SetStatus, + set_title: SetTitle, + set_suggested_prompts: SetSuggestedPrompts, + get_thread_context: GetThreadContext, + save_thread_context: SaveThreadContext, + context: BoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + called["value"] = True + + request = BoltRequest(body=thread_context_changed_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_user_message(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.message("") + def handle_message( + say: Say, + set_status: SetStatus, + set_title: SetTitle, + set_suggested_prompts: SetSuggestedPrompts, + get_thread_context: GetThreadContext, + save_thread_context: SaveThreadContext, + context: BoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + try: + set_status("is typing...") + say("Here you are!") + called["value"] = True + except Exception as e: + say(f"Oops, something went wrong (error: {e})") + + request = BoltRequest(body=user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_user_message_with_assistant_thread(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.message("") + def handle_message( + say: Say, + set_status: SetStatus, + set_title: SetTitle, + set_suggested_prompts: SetSuggestedPrompts, + get_thread_context: GetThreadContext, + save_thread_context: SaveThreadContext, + context: BoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + try: + set_status("is typing...") + say("Here you are!") + called["value"] = True + except Exception as e: + say(f"Oops, something went wrong (error: {e})") + + request = BoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_message_changed(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("message") + def handle_message_event( + say: Say, + set_status: SetStatus, + set_title: SetTitle, + set_suggested_prompts: SetSuggestedPrompts, + get_thread_context: GetThreadContext, + save_thread_context: SaveThreadContext, + context: BoltContext, + ): + assert context.thread_ts is None + assert say.thread_ts == context.thread_ts + assert set_status is None + assert set_title is None + assert set_suggested_prompts is None + assert get_thread_context is None + assert save_thread_context is None + called["value"] = True + + request = BoltRequest(body=message_changed_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_channel_user_message(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("message") + def handle_message_event( + say: Say, + set_status: SetStatus, + set_title: SetTitle, + set_suggested_prompts: SetSuggestedPrompts, + get_thread_context: GetThreadContext, + save_thread_context: SaveThreadContext, + context: BoltContext, + ): + assert context.thread_ts is None + assert say.thread_ts == context.thread_ts + assert set_status is None + assert set_title is None + assert set_suggested_prompts is None + assert get_thread_context is None + assert save_thread_context is None + called["value"] = True + + request = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_channel_message_changed(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("message") + def handle_message_event( + say: Say, + set_status: SetStatus, + set_title: SetTitle, + set_suggested_prompts: SetSuggestedPrompts, + get_thread_context: GetThreadContext, + save_thread_context: SaveThreadContext, + context: BoltContext, + ): + assert context.thread_ts is None + assert say.thread_ts == context.thread_ts + assert set_status is None + assert set_title is None + assert set_suggested_prompts is None + assert get_thread_context is None + assert save_thread_context is None + called["value"] = True + + request = BoltRequest(body=channel_message_changed_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index b131b4e38..c6d04474d 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -1,4 +1,5 @@ import asyncio +import time import pytest from slack_sdk.web.async_client import AsyncWebClient @@ -17,6 +18,13 @@ from tests.utils import remove_os_env_temporarily, restore_os_env +async def assert_target_called(called: dict, timeout: float = 0.5): + deadline = time.time() + timeout + while called["value"] is not True and time.time() < deadline: + await asyncio.sleep(0.1) + assert called["value"] is True + + class TestAsyncEventsAssistant: valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" @@ -29,6 +37,7 @@ class TestAsyncEventsAssistant: def setup_teardown(self): old_os_env = remove_os_env_temporarily() setup_mock_web_api_server_async(self) + try: yield # run the test here finally: @@ -36,25 +45,22 @@ def setup_teardown(self): restore_os_env(old_os_env) @pytest.mark.asyncio - async def test_assistant_events(self): + async def test_thread_started(self): app = AsyncApp(client=self.web_client) - assistant = AsyncAssistant() - - state = {"called": False} - - async def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - await asyncio.sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False + called = {"value": False} @assistant.thread_started - async def start_thread(say: AsyncSay, set_suggested_prompts: AsyncSetSuggestedPrompts, context: AsyncBoltContext): + async def start_thread( + say: AsyncSay, + set_suggested_prompts: AsyncSetSuggestedPrompts, + set_status: AsyncSetStatus, + context: AsyncBoltContext, + ): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" + assert set_status.thread_ts == context.thread_ts + assert say.thread_ts == context.thread_ts await say("Hi, how can I help you today?") await set_suggested_prompts( prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}] @@ -63,58 +69,146 @@ async def start_thread(say: AsyncSay, set_suggested_prompts: AsyncSetSuggestedPr prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}], title="foo", ) - state["called"] = True + called["value"] = True + + app.assistant(assistant) + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_thread_context_changed(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} @assistant.thread_context_changed - async def handle_user_message(context: AsyncBoltContext): + async def handle_thread_context_changed(context: AsyncBoltContext): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" - state["called"] = True + called["value"] = True + + app.assistant(assistant) + + request = AsyncBoltRequest(body=thread_context_changed_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_user_message(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} @assistant.user_message async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context: AsyncBoltContext): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts try: await set_status("is typing...") await say("Here you are!") - state["called"] = True + called["value"] = True except Exception as e: - await say(f"Oops, something went wrong (error: {e}") + await say(f"Oops, something went wrong (error: {e})") app.assistant(assistant) - request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called() + await assert_target_called(called) - request = AsyncBoltRequest(body=thread_context_changed_event_body, mode="socket_mode") - response = await app.async_dispatch(request) - assert response.status == 200 - await assert_target_called() + @pytest.mark.asyncio + async def test_user_message_with_assistant_thread(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} - request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") - response = await app.async_dispatch(request) - assert response.status == 200 - await assert_target_called() + @assistant.user_message + async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context: AsyncBoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + try: + await set_status("is typing...") + await say("Here you are!") + called["value"] = True + except Exception as e: + await say(f"Oops, something went wrong (error: {e})") + + app.assistant(assistant) request = AsyncBoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called() + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_message_changed(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} + + @assistant.user_message + async def handle_user_message(): + called["value"] = True + + @assistant.bot_message + async def handle_bot_message(): + called["value"] = True + + app.assistant(assistant) request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 + assert called["value"] is False + + @pytest.mark.asyncio + async def test_channel_user_message_ignored(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} + + @assistant.user_message + async def handle_user_message(): + called["value"] = True + + @assistant.bot_message + async def handle_bot_message(): + called["value"] = True + + app.assistant(assistant) request = AsyncBoltRequest(body=channel_user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 404 + assert called["value"] is False + + @pytest.mark.asyncio + async def test_channel_message_changed_ignored(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} + + @assistant.user_message + async def handle_user_message(): + called["value"] = True + + @assistant.bot_message + async def handle_bot_message(): + called["value"] = True + + app.assistant(assistant) request = AsyncBoltRequest(body=channel_message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 404 + assert called["value"] is False def build_payload(event: dict) -> dict: diff --git a/tests/scenario_tests_async/test_events_assistant_without_middleware.py b/tests/scenario_tests_async/test_events_assistant_without_middleware.py new file mode 100644 index 000000000..92f488ff3 --- /dev/null +++ b/tests/scenario_tests_async/test_events_assistant_without_middleware.py @@ -0,0 +1,268 @@ +import pytest +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.app.async_app import AsyncApp +from slack_bolt.context.async_context import AsyncBoltContext +from slack_bolt.context.say.async_say import AsyncSay +from slack_bolt.context.set_status.async_set_status import AsyncSetStatus +from slack_bolt.context.set_title.async_set_title import AsyncSetTitle +from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts +from slack_bolt.context.get_thread_context.async_get_thread_context import AsyncGetThreadContext +from slack_bolt.context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext +from slack_bolt.request.async_request import AsyncBoltRequest +from tests.mock_web_api_server import ( + cleanup_mock_web_api_server_async, + setup_mock_web_api_server_async, +) +from tests.scenario_tests_async.test_events_assistant import ( + assert_target_called, + channel_message_changed_event_body, + channel_user_message_event_body, + message_changed_event_body, + thread_context_changed_event_body, + thread_started_event_body, + user_message_event_body, + user_message_event_body_with_assistant_thread, +) +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestAsyncEventsAssistantWithoutMiddleware: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = AsyncWebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) + + try: + yield + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) + + @pytest.mark.asyncio + async def test_thread_started(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("assistant_thread_started") + async def handle_assistant_thread_started( + say: AsyncSay, + set_status: AsyncSetStatus, + set_title: AsyncSetTitle, + set_suggested_prompts: AsyncSetSuggestedPrompts, + get_thread_context: AsyncGetThreadContext, + save_thread_context: AsyncSaveThreadContext, + context: AsyncBoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + await say("Hi, how can I help you today?") + await set_suggested_prompts( + prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}] + ) + called["value"] = True + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_thread_context_changed(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("assistant_thread_context_changed") + async def handle_assistant_thread_context_changed( + say: AsyncSay, + set_status: AsyncSetStatus, + set_title: AsyncSetTitle, + set_suggested_prompts: AsyncSetSuggestedPrompts, + get_thread_context: AsyncGetThreadContext, + save_thread_context: AsyncSaveThreadContext, + context: AsyncBoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + called["value"] = True + + request = AsyncBoltRequest(body=thread_context_changed_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_user_message(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.message("") + async def handle_message( + say: AsyncSay, + set_status: AsyncSetStatus, + set_title: AsyncSetTitle, + set_suggested_prompts: AsyncSetSuggestedPrompts, + get_thread_context: AsyncGetThreadContext, + save_thread_context: AsyncSaveThreadContext, + context: AsyncBoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + try: + await set_status("is typing...") + await say("Here you are!") + called["value"] = True + except Exception as e: + await say(f"Oops, something went wrong (error: {e})") + + request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_user_message_with_assistant_thread(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.message("") + async def handle_message( + say: AsyncSay, + set_status: AsyncSetStatus, + set_title: AsyncSetTitle, + set_suggested_prompts: AsyncSetSuggestedPrompts, + get_thread_context: AsyncGetThreadContext, + save_thread_context: AsyncSaveThreadContext, + context: AsyncBoltContext, + ): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + assert set_status is not None + assert set_title is not None + assert set_suggested_prompts is not None + assert get_thread_context is not None + assert save_thread_context is not None + try: + await set_status("is typing...") + await say("Here you are!") + called["value"] = True + except Exception as e: + await say(f"Oops, something went wrong (error: {e})") + + request = AsyncBoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_message_changed(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("message") + async def handle_message_event( + say: AsyncSay, + set_status: AsyncSetStatus, + set_title: AsyncSetTitle, + set_suggested_prompts: AsyncSetSuggestedPrompts, + get_thread_context: AsyncGetThreadContext, + save_thread_context: AsyncSaveThreadContext, + context: AsyncBoltContext, + ): + assert context.thread_ts is None + assert say.thread_ts == context.thread_ts + assert set_status is None + assert set_title is None + assert set_suggested_prompts is None + assert get_thread_context is None + assert save_thread_context is None + called["value"] = True + + request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_channel_user_message(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("message") + async def handle_message_event( + say: AsyncSay, + set_status: AsyncSetStatus, + set_title: AsyncSetTitle, + set_suggested_prompts: AsyncSetSuggestedPrompts, + get_thread_context: AsyncGetThreadContext, + save_thread_context: AsyncSaveThreadContext, + context: AsyncBoltContext, + ): + assert context.thread_ts is None + assert say.thread_ts == context.thread_ts + assert set_status is None + assert set_title is None + assert set_suggested_prompts is None + assert get_thread_context is None + assert save_thread_context is None + called["value"] = True + + request = AsyncBoltRequest(body=channel_user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_channel_message_changed(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("message") + async def handle_message_event( + say: AsyncSay, + set_status: AsyncSetStatus, + set_title: AsyncSetTitle, + set_suggested_prompts: AsyncSetSuggestedPrompts, + get_thread_context: AsyncGetThreadContext, + save_thread_context: AsyncSaveThreadContext, + context: AsyncBoltContext, + ): + assert context.thread_ts is None + assert say.thread_ts == context.thread_ts + assert set_status is None + assert set_title is None + assert set_suggested_prompts is None + assert get_thread_context is None + assert save_thread_context is None + called["value"] = True + + request = AsyncBoltRequest(body=channel_message_changed_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) From 785b81352ccf2d382c430c7cf57bc05c8f167599 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 17 Mar 2026 10:01:06 -0700 Subject: [PATCH 198/282] fix(assistant): improve middleware dispatch and inject kwargs in middleware (#1456) --- slack_bolt/app/app.py | 27 +-- slack_bolt/app/async_app.py | 24 +-- slack_bolt/context/async_context.py | 2 +- slack_bolt/context/context.py | 2 +- slack_bolt/middleware/__init__.py | 2 + slack_bolt/middleware/assistant/assistant.py | 11 + .../middleware/assistant/async_assistant.py | 11 + slack_bolt/middleware/async_builtins.py | 2 + .../attaching_agent_kwargs/__init__.py | 5 + .../async_attaching_agent_kwargs.py | 39 ++++ .../attaching_agent_kwargs.py | 33 +++ slack_bolt/request/internals.py | 39 +--- tests/scenario_tests/test_events_assistant.py | 71 +++++++ ...est_events_assistant_without_middleware.py | 31 ++- .../test_events_assistant.py | 127 +++++++++++ ...est_events_assistant_without_middleware.py | 32 ++- .../attaching_agent_kwargs/__init__.py | 0 .../test_attaching_agent_kwargs.py | 64 ++++++ tests/slack_bolt/request/test_internals.py | 201 ++++++++++++++++++ .../attaching_agent_kwargs/__init__.py | 0 .../test_async_attaching_agent_kwargs.py | 69 ++++++ 21 files changed, 715 insertions(+), 77 deletions(-) create mode 100644 slack_bolt/middleware/attaching_agent_kwargs/__init__.py create mode 100644 slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py create mode 100644 slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py create mode 100644 tests/slack_bolt/middleware/attaching_agent_kwargs/__init__.py create mode 100644 tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py create mode 100644 tests/slack_bolt_async/middleware/attaching_agent_kwargs/__init__.py create mode 100644 tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index fcf5bb788..566eb82d7 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -22,7 +22,6 @@ from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore -from slack_bolt.context.assistant.assistant_utilities import AssistantUtilities from slack_bolt.error import BoltError, BoltUnhandledRequestError from slack_bolt.lazy_listener.thread_runner import ThreadLazyListenerRunner from slack_bolt.listener.builtins import TokenRevocationListeners @@ -70,6 +69,7 @@ IgnoringSelfEvents, CustomMiddleware, AttachingFunctionToken, + AttachingAgentKwargs, ) from slack_bolt.middleware.assistant import Assistant from slack_bolt.middleware.message_listener_matches import MessageListenerMatches @@ -83,10 +83,6 @@ from slack_bolt.oauth.internals import select_consistent_installation_store from slack_bolt.oauth.oauth_settings import OAuthSettings from slack_bolt.request import BoltRequest -from slack_bolt.request.payload_utils import ( - is_assistant_event, - to_event, -) from slack_bolt.response import BoltResponse from slack_bolt.util.utils import ( create_web_client, @@ -137,6 +133,7 @@ def __init__( listener_executor: Optional[Executor] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, + attaching_agent_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -357,6 +354,7 @@ def message_hello(message, say): listener_executor = ThreadPoolExecutor(max_workers=5) self._assistant_thread_context_store = assistant_thread_context_store + self._attaching_agent_kwargs_enabled = attaching_agent_kwargs_enabled self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( @@ -841,10 +839,13 @@ def ask_for_introduction(event, say): middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) + if self._attaching_agent_kwargs_enabled: + middleware.insert(0, AttachingAgentKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -902,6 +903,8 @@ def __call__(*args, **kwargs): primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) + if self._attaching_agent_kwargs_enabled: + middleware.insert(0, AttachingAgentKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -1398,20 +1401,6 @@ def _init_context(self, req: BoltRequest): # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner - # For AI Agents & Assistants - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=to_event(req.body), # type: ignore[arg-type] - context=req.context, - thread_context_store=self._assistant_thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - @staticmethod def _to_listener_functions( kwargs: dict, diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 62c491084..9cd8c911f 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -8,7 +8,6 @@ from aiohttp import web from slack_bolt.app.async_server import AsyncSlackAppServer -from slack_bolt.context.assistant.async_assistant_utilities import AsyncAssistantUtilities from slack_bolt.context.assistant.thread_context_store.async_store import ( AsyncAssistantThreadContextStore, ) @@ -30,7 +29,6 @@ AsyncMessageListenerMatches, ) from slack_bolt.oauth.async_internals import select_consistent_installation_store -from slack_bolt.request.payload_utils import is_assistant_event, to_event from slack_bolt.util.utils import get_name_for_callable, is_callable_coroutine from slack_bolt.workflows.step.async_step import ( AsyncWorkflowStep, @@ -88,6 +86,7 @@ AsyncIgnoringSelfEvents, AsyncUrlVerification, AsyncAttachingFunctionToken, + AsyncAttachingAgentKwargs, ) from slack_bolt.middleware.async_custom_middleware import ( AsyncMiddleware, @@ -143,6 +142,7 @@ def __init__( verification_token: Optional[str] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, + attaching_agent_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -363,6 +363,7 @@ async def message_hello(message, say): # async function self._async_listeners: List[AsyncListener] = [] self._assistant_thread_context_store = assistant_thread_context_store + self._attaching_agent_kwargs_enabled = attaching_agent_kwargs_enabled self._process_before_response = process_before_response self._async_listener_runner = AsyncioListenerRunner( @@ -866,10 +867,13 @@ async def ask_for_introduction(event, say): middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, True, base_logger=self._base_logger) + if self._attaching_agent_kwargs_enabled: + middleware.insert(0, AsyncAttachingAgentKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -930,6 +934,8 @@ def __call__(*args, **kwargs): asyncio=True, base_logger=self._base_logger, ) + if self._attaching_agent_kwargs_enabled: + middleware.insert(0, AsyncAttachingAgentKwargs(self._assistant_thread_context_store)) middleware.insert(0, AsyncMessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -1431,20 +1437,6 @@ def _init_context(self, req: AsyncBoltRequest): # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner - # For AI Agents & Assistants - if is_assistant_event(req.body): - assistant = AsyncAssistantUtilities( - payload=to_event(req.body), # type: ignore[arg-type] - context=req.context, - thread_context_store=self._assistant_thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - @staticmethod def _to_listener_functions( kwargs: dict, diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index 47eb4744e..631f74a82 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -110,7 +110,7 @@ async def handle_button_clicks(ack, say): Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id) return self["say"] @property diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index 31edf2891..48df4ad32 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -111,7 +111,7 @@ def handle_button_clicks(ack, say): Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] @property diff --git a/slack_bolt/middleware/__init__.py b/slack_bolt/middleware/__init__.py index 0e4044f99..7b51fb239 100644 --- a/slack_bolt/middleware/__init__.py +++ b/slack_bolt/middleware/__init__.py @@ -17,6 +17,7 @@ from .ssl_check import SslCheck from .url_verification import UrlVerification from .attaching_function_token import AttachingFunctionToken +from .attaching_agent_kwargs import AttachingAgentKwargs builtin_middleware_classes = [ SslCheck, @@ -41,5 +42,6 @@ "SslCheck", "UrlVerification", "AttachingFunctionToken", + "AttachingAgentKwargs", "builtin_middleware_classes", ] diff --git a/slack_bolt/middleware/assistant/assistant.py b/slack_bolt/middleware/assistant/assistant.py index d61386105..9696e826e 100644 --- a/slack_bolt/middleware/assistant/assistant.py +++ b/slack_bolt/middleware/assistant/assistant.py @@ -7,6 +7,7 @@ from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore from slack_bolt.listener_matcher.builtins import build_listener_matcher +from slack_bolt.middleware.attaching_agent_kwargs import AttachingAgentKwargs from slack_bolt.request.request import BoltRequest from slack_bolt.response.response import BoltResponse from slack_bolt.listener_matcher import CustomListenerMatcher @@ -236,6 +237,15 @@ def process( # type: ignore[return] if listeners is not None: for listener in listeners: if listener.matches(req=req, resp=resp): + middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) + if next_was_not_called: + if middleware_resp is not None: + return middleware_resp + # The listener middleware didn't call next(). + # Skip this listener and try the next one. + continue + if middleware_resp is not None: + resp = middleware_resp return listener_runner.run( request=req, response=resp, @@ -262,6 +272,7 @@ def build_listener( return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AttachingAgentKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) diff --git a/slack_bolt/middleware/assistant/async_assistant.py b/slack_bolt/middleware/assistant/async_assistant.py index ae82595a8..d841e2de0 100644 --- a/slack_bolt/middleware/assistant/async_assistant.py +++ b/slack_bolt/middleware/assistant/async_assistant.py @@ -8,6 +8,7 @@ from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner from slack_bolt.listener_matcher.builtins import build_listener_matcher +from slack_bolt.middleware.attaching_agent_kwargs.async_attaching_agent_kwargs import AsyncAttachingAgentKwargs from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse from slack_bolt.error import BoltError @@ -265,6 +266,15 @@ async def async_process( # type: ignore[return] if listeners is not None: for listener in listeners: if listener is not None and await listener.async_matches(req=req, resp=resp): + middleware_resp, next_was_not_called = await listener.run_async_middleware(req=req, resp=resp) + if next_was_not_called: + if middleware_resp is not None: + return middleware_resp + # The listener middleware didn't call next(). + # Skip this listener and try the next one. + continue + if middleware_resp is not None: + resp = middleware_resp return await listener_runner.run( request=req, response=resp, @@ -291,6 +301,7 @@ def build_listener( return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AsyncAttachingAgentKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) diff --git a/slack_bolt/middleware/async_builtins.py b/slack_bolt/middleware/async_builtins.py index d2d82c1fb..755b55c20 100644 --- a/slack_bolt/middleware/async_builtins.py +++ b/slack_bolt/middleware/async_builtins.py @@ -10,6 +10,7 @@ AsyncMessageListenerMatches, ) from .attaching_function_token.async_attaching_function_token import AsyncAttachingFunctionToken +from .attaching_agent_kwargs.async_attaching_agent_kwargs import AsyncAttachingAgentKwargs __all__ = [ "AsyncIgnoringSelfEvents", @@ -18,4 +19,5 @@ "AsyncUrlVerification", "AsyncMessageListenerMatches", "AsyncAttachingFunctionToken", + "AsyncAttachingAgentKwargs", ] diff --git a/slack_bolt/middleware/attaching_agent_kwargs/__init__.py b/slack_bolt/middleware/attaching_agent_kwargs/__init__.py new file mode 100644 index 000000000..98926fc14 --- /dev/null +++ b/slack_bolt/middleware/attaching_agent_kwargs/__init__.py @@ -0,0 +1,5 @@ +from .attaching_agent_kwargs import AttachingAgentKwargs + +__all__ = [ + "AttachingAgentKwargs", +] diff --git a/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py new file mode 100644 index 000000000..0b43c21ce --- /dev/null +++ b/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py @@ -0,0 +1,39 @@ +from typing import Optional, Callable, Awaitable + +from slack_bolt.context.assistant.async_assistant_utilities import AsyncAssistantUtilities +from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore +from slack_bolt.middleware.async_middleware import AsyncMiddleware +from slack_bolt.request.async_request import AsyncBoltRequest +from slack_bolt.request.payload_utils import is_assistant_event, to_event +from slack_bolt.response import BoltResponse + + +class AsyncAttachingAgentKwargs(AsyncMiddleware): + + thread_context_store: Optional[AsyncAssistantThreadContextStore] + + def __init__(self, thread_context_store: Optional[AsyncAssistantThreadContextStore] = None): + self.thread_context_store = thread_context_store + + async def async_process( + self, + *, + req: AsyncBoltRequest, + resp: BoltResponse, + next: Callable[[], Awaitable[BoltResponse]], + ) -> Optional[BoltResponse]: + event = to_event(req.body) + if event is not None: + if is_assistant_event(req.body): + assistant = AsyncAssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + return await next() diff --git a/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py new file mode 100644 index 000000000..4963ea67d --- /dev/null +++ b/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py @@ -0,0 +1,33 @@ +from typing import Optional, Callable + +from slack_bolt.context.assistant.assistant_utilities import AssistantUtilities +from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore +from slack_bolt.middleware import Middleware +from slack_bolt.request.payload_utils import is_assistant_event, to_event +from slack_bolt.request.request import BoltRequest +from slack_bolt.response.response import BoltResponse + + +class AttachingAgentKwargs(Middleware): + + thread_context_store: Optional[AssistantThreadContextStore] + + def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = None): + self.thread_context_store = thread_context_store + + def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: + event = to_event(req.body) + if event is not None: + if is_assistant_event(req.body): + assistant = AssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_status"] = assistant.set_status + req.context["set_title"] = assistant.set_title + req.context["set_suggested_prompts"] = assistant.set_suggested_prompts + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + return next() diff --git a/slack_bolt/request/internals.py b/slack_bolt/request/internals.py index e6a32db0d..466f5daaf 100644 --- a/slack_bolt/request/internals.py +++ b/slack_bolt/request/internals.py @@ -3,7 +3,6 @@ from urllib.parse import parse_qsl, parse_qs from slack_bolt.context import BoltContext -from slack_bolt.request.payload_utils import is_assistant_event def parse_query(query: Optional[Union[str, Dict[str, str], Dict[str, Sequence[str]]]]) -> Dict[str, Sequence[str]]: @@ -215,33 +214,17 @@ def extract_channel_id(payload: Dict[str, Any]) -> Optional[str]: def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]: - # This utility initially supports only the use cases for AI assistants, but it may be fine to add more patterns. - # That said, note that thread_ts is always required for assistant threads, but it's not for channels. - # Thus, blindly setting this thread_ts to say utility can break existing apps' behaviors. - # - # The BoltAgent class handles non-assistant thread_ts separately by reading from the event directly, - # allowing it to work correctly without affecting say() behavior. - if is_assistant_event(payload): - event = payload["event"] - if ( - event.get("assistant_thread") is not None - and event["assistant_thread"].get("channel_id") is not None - and event["assistant_thread"].get("thread_ts") is not None - ): - # assistant_thread_started, assistant_thread_context_changed - # "assistant_thread" property can exist for message event without channel_id and thread_ts - # Thus, the above if check verifies these properties exist - return event["assistant_thread"]["thread_ts"] - elif event.get("channel") is not None: - if event.get("thread_ts") is not None: - # message in an assistant thread - return event["thread_ts"] - elif event.get("message", {}).get("thread_ts") is not None: - # message_changed - return event["message"]["thread_ts"] - elif event.get("previous_message", {}).get("thread_ts") is not None: - # message_deleted - return event["previous_message"]["thread_ts"] + thread_ts = payload.get("thread_ts") + if thread_ts is not None: + return thread_ts + if payload.get("event") is not None: + return extract_thread_ts(payload["event"]) + if isinstance(payload.get("assistant_thread"), dict): + return extract_thread_ts(payload["assistant_thread"]) + if isinstance(payload.get("message"), dict): + return extract_thread_ts(payload["message"]) + if isinstance(payload.get("previous_message"), dict): + return extract_thread_ts(payload["previous_message"]) return None diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index 3372380fd..5bc270d86 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -1,8 +1,13 @@ import time +from time import sleep +from typing import Callable from slack_sdk.web import WebClient from slack_bolt import App, BoltRequest, Assistant, Say, SetSuggestedPrompts, SetStatus, BoltContext +from slack_bolt.middleware import Middleware +from slack_bolt.request import BoltRequest as BoltRequestType +from slack_bolt.response import BoltResponse from tests.mock_web_api_server import ( setup_mock_web_api_server, cleanup_mock_web_api_server, @@ -184,6 +189,72 @@ def handle_bot_message(): assert response.status == 404 assert called["value"] is False + def test_assistant_with_custom_listener_middleware(self): + app = App(client=self.web_client) + assistant = Assistant() + handler_called = {"value": False} + middleware_called = {"value": False} + + class TestMiddleware(Middleware): + def process(self, *, req: BoltRequestType, resp: BoltResponse, next: Callable[[], BoltResponse]): + middleware_called["value"] = True + # Verify assistant utilities are available + assert req.context.get("set_status") is not None + assert req.context.get("set_title") is not None + assert req.context.get("set_suggested_prompts") is not None + assert req.context.get("get_thread_context") is not None + assert req.context.get("save_thread_context") is not None + return next() + + @assistant.thread_started(middleware=[TestMiddleware()]) + def start_thread(): + handler_called["value"] = True + + @assistant.user_message(middleware=[TestMiddleware()]) + def handle_user_message(): + handler_called["value"] = True + + app.assistant(assistant) + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(handler_called) + assert_target_called(middleware_called) + + handler_called = {"value": False} + middleware_called = {"value": False} + + request = BoltRequest(body=user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(handler_called) + assert_target_called(middleware_called) + + def test_assistant_custom_middleware_can_short_circuit(self): + app = App(client=self.web_client) + assistant = Assistant() + handler_called = {"value": False} + middleware_called = {"value": False} + + class BlockingMiddleware(Middleware): + def process(self, *, req: BoltRequestType, resp: BoltResponse, next: Callable[[], BoltResponse]): + middleware_called["value"] = True + # Intentionally not calling next() to short-circuit + return BoltResponse(status=200) + + @assistant.thread_started(middleware=[BlockingMiddleware()]) + def start_thread(say: Say, context: BoltContext): + handler_called["value"] = True + + app.assistant(assistant) + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(middleware_called) + assert handler_called["value"] is False + def build_payload(event: dict) -> dict: return { diff --git a/tests/scenario_tests/test_events_assistant_without_middleware.py b/tests/scenario_tests/test_events_assistant_without_middleware.py index 5307aa4c6..36d86c43a 100644 --- a/tests/scenario_tests/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests/test_events_assistant_without_middleware.py @@ -178,8 +178,8 @@ def handle_message_event( save_thread_context: SaveThreadContext, context: BoltContext, ): - assert context.thread_ts is None - assert say.thread_ts == context.thread_ts + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == None assert set_status is None assert set_title is None assert set_suggested_prompts is None @@ -206,8 +206,8 @@ def handle_message_event( save_thread_context: SaveThreadContext, context: BoltContext, ): - assert context.thread_ts is None - assert say.thread_ts == context.thread_ts + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == None assert set_status is None assert set_title is None assert set_suggested_prompts is None @@ -234,8 +234,8 @@ def handle_message_event( save_thread_context: SaveThreadContext, context: BoltContext, ): - assert context.thread_ts is None - assert say.thread_ts == context.thread_ts + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == None assert set_status is None assert set_title is None assert set_suggested_prompts is None @@ -247,3 +247,22 @@ def handle_message_event( response = app.dispatch(request) assert response.status == 200 assert_target_called(called) + + def test_assistant_events_agent_kwargs_disabled(self): + app = App(client=self.web_client, attaching_agent_kwargs_enabled=False) + + called = {"value": False} + + @app.event("assistant_thread_started") + def start_thread(context: BoltContext): + assert context.get("set_status") is None + assert context.get("set_title") is None + assert context.get("set_suggested_prompts") is None + assert context.get("get_thread_context") is None + assert context.get("save_thread_context") is None + called["value"] = True + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index c6d04474d..87b337536 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -1,5 +1,6 @@ import asyncio import time +from typing import Awaitable, Callable, Optional import pytest from slack_sdk.web.async_client import AsyncWebClient @@ -10,7 +11,9 @@ from slack_bolt.context.set_status.async_set_status import AsyncSetStatus from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts from slack_bolt.middleware.assistant.async_assistant import AsyncAssistant +from slack_bolt.middleware.async_middleware import AsyncMiddleware from slack_bolt.request.async_request import AsyncBoltRequest +from slack_bolt.response import BoltResponse from tests.mock_web_api_server import ( cleanup_mock_web_api_server_async, setup_mock_web_api_server_async, @@ -210,6 +213,130 @@ async def handle_bot_message(): assert response.status == 404 assert called["value"] is False + @pytest.mark.asyncio + async def test_assistant_events_kwargs_disabled(self): + app = AsyncApp(client=self.web_client, attaching_agent_kwargs_enabled=False) + + state = {"called": False} + + async def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + await asyncio.sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + @app.event("assistant_thread_started") + async def start_thread(context: AsyncBoltContext): + assert context.get("set_status") is None + assert context.get("set_title") is None + assert context.get("set_suggested_prompts") is None + assert context.get("get_thread_context") is None + assert context.get("save_thread_context") is None + state["called"] = True + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + + @pytest.mark.asyncio + async def test_assistant_with_custom_listener_middleware(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + + state = {"called": False, "middleware_called": False} + + async def assert_target_called(): + count = 0 + while state["called"] is False and count < 20: + await asyncio.sleep(0.1) + count += 1 + assert state["called"] is True + state["called"] = False + + class TestAsyncMiddleware(AsyncMiddleware): + async def async_process( + self, + *, + req: AsyncBoltRequest, + resp: BoltResponse, + next: Callable[[], Awaitable[BoltResponse]], + ) -> Optional[BoltResponse]: + state["middleware_called"] = True + # Verify assistant utilities are available (set by _AsyncAssistantMiddleware before this) + assert req.context.get("set_status") is not None + assert req.context.get("set_title") is not None + assert req.context.get("set_suggested_prompts") is not None + assert req.context.get("get_thread_context") is not None + assert req.context.get("save_thread_context") is not None + return await next() + + @assistant.thread_started(middleware=[TestAsyncMiddleware()]) + async def start_thread(say: AsyncSay, set_suggested_prompts: AsyncSetSuggestedPrompts, context: AsyncBoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + await say("Hi, how can I help you today?") + await set_suggested_prompts( + prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}] + ) + state["called"] = True + + @assistant.user_message(middleware=[TestAsyncMiddleware()]) + async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context: AsyncBoltContext): + assert context.channel_id == "D111" + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == context.thread_ts + await set_status("is typing...") + await say("Here you are!") + state["called"] = True + + app.assistant(assistant) + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + assert state["middleware_called"] is True + state["middleware_called"] = False + + request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + assert state["middleware_called"] is True + + @pytest.mark.asyncio + async def test_assistant_custom_middleware_can_short_circuit(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + + state = {"handler_called": False} + + class BlockingAsyncMiddleware(AsyncMiddleware): + async def async_process( + self, + *, + req: AsyncBoltRequest, + resp: BoltResponse, + next: Callable[[], Awaitable[BoltResponse]], + ) -> Optional[BoltResponse]: + # Intentionally not calling next() to short-circuit + return BoltResponse(status=200) + + @assistant.thread_started(middleware=[BlockingAsyncMiddleware()]) + async def start_thread(say: AsyncSay, context: AsyncBoltContext): + state["handler_called"] = True + + app.assistant(assistant) + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + assert state["handler_called"] is False + def build_payload(event: dict) -> dict: return { diff --git a/tests/scenario_tests_async/test_events_assistant_without_middleware.py b/tests/scenario_tests_async/test_events_assistant_without_middleware.py index 92f488ff3..be6c2b166 100644 --- a/tests/scenario_tests_async/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests_async/test_events_assistant_without_middleware.py @@ -195,8 +195,8 @@ async def handle_message_event( save_thread_context: AsyncSaveThreadContext, context: AsyncBoltContext, ): - assert context.thread_ts is None - assert say.thread_ts == context.thread_ts + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == None assert set_status is None assert set_title is None assert set_suggested_prompts is None @@ -224,8 +224,8 @@ async def handle_message_event( save_thread_context: AsyncSaveThreadContext, context: AsyncBoltContext, ): - assert context.thread_ts is None - assert say.thread_ts == context.thread_ts + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == None assert set_status is None assert set_title is None assert set_suggested_prompts is None @@ -253,8 +253,8 @@ async def handle_message_event( save_thread_context: AsyncSaveThreadContext, context: AsyncBoltContext, ): - assert context.thread_ts is None - assert say.thread_ts == context.thread_ts + assert context.thread_ts == "1726133698.626339" + assert say.thread_ts == None assert set_status is None assert set_title is None assert set_suggested_prompts is None @@ -266,3 +266,23 @@ async def handle_message_event( response = await app.async_dispatch(request) assert response.status == 200 await assert_target_called(called) + + @pytest.mark.asyncio + async def test_assistant_events_agent_kwargs_disabled(self): + app = AsyncApp(client=self.web_client, attaching_agent_kwargs_enabled=False) + + called = {"value": False} + + @app.event("assistant_thread_started") + async def start_thread(context: AsyncBoltContext): + assert context.get("set_status") is None + assert context.get("set_title") is None + assert context.get("set_suggested_prompts") is None + assert context.get("get_thread_context") is None + assert context.get("save_thread_context") is None + called["value"] = True + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) diff --git a/tests/slack_bolt/middleware/attaching_agent_kwargs/__init__.py b/tests/slack_bolt/middleware/attaching_agent_kwargs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py b/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py new file mode 100644 index 000000000..f56bd2e62 --- /dev/null +++ b/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py @@ -0,0 +1,64 @@ +from slack_sdk import WebClient + +from slack_bolt.middleware.attaching_agent_kwargs import AttachingAgentKwargs +from slack_bolt.request import BoltRequest +from slack_bolt.response import BoltResponse +from tests.scenario_tests.test_events_assistant import ( + thread_started_event_body, + user_message_event_body, + channel_user_message_event_body, +) + + +def next(): + return BoltResponse(status=200) + + +AGENT_KWARGS = ("say", "set_status", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") + + +class TestAttachingAgentKwargs: + def test_assistant_event_attaches_kwargs(self): + middleware = AttachingAgentKwargs() + req = BoltRequest(body=thread_started_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key in req.context, f"{key} should be set on context" + assert req.context["say"].thread_ts == "1726133698.626339" + + def test_user_message_event_attaches_kwargs(self): + middleware = AttachingAgentKwargs() + req = BoltRequest(body=user_message_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key in req.context, f"{key} should be set on context" + assert req.context["say"].thread_ts == "1726133698.626339" + + def test_non_assistant_event_does_not_attach_kwargs(self): + middleware = AttachingAgentKwargs() + req = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key not in req.context, f"{key} should not be set on context" + + def test_non_event_does_not_attach_kwargs(self): + middleware = AttachingAgentKwargs() + req = BoltRequest(body="payload={}", headers={}) + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key not in req.context, f"{key} should not be set on context" diff --git a/tests/slack_bolt/request/test_internals.py b/tests/slack_bolt/request/test_internals.py index 752fa6d2d..0b267e3de 100644 --- a/tests/slack_bolt/request/test_internals.py +++ b/tests/slack_bolt/request/test_internals.py @@ -13,6 +13,7 @@ extract_actor_team_id, extract_actor_user_id, extract_function_execution_id, + extract_thread_ts, ) @@ -111,6 +112,196 @@ def teardown_method(self): }, ] + thread_ts_event_requests = [ + { + "event": { + "type": "app_mention", + "channel": "C111", + "user": "U111", + "ts": "123.420", + "thread_ts": "123.456", + }, + }, + { + "event": { + "type": "message", + "channel": "C111", + "user": "U111", + "ts": "123.420", + "thread_ts": "123.456", + }, + }, + { + "event": { + "type": "message", + "subtype": "bot_message", + "channel": "C111", + "bot_id": "B111", + "ts": "123.420", + "thread_ts": "123.456", + }, + }, + { + "event": { + "type": "message", + "subtype": "file_share", + "channel": "C111", + "user": "U111", + "ts": "123.420", + "thread_ts": "123.456", + }, + }, + { + "event": { + "type": "message", + "subtype": "thread_broadcast", + "channel": "C111", + "user": "U111", + "ts": "123.420", + "thread_ts": "123.456", + "root": {"thread_ts": "123.420"}, + }, + }, + { + "event": { + "type": "link_shared", + "channel": "C111", + "user": "U111", + "thread_ts": "123.456", + "links": [{"url": "https://example.com"}], + }, + }, + { + "event": { + "type": "message", + "subtype": "message_changed", + "channel": "C111", + "message": { + "type": "message", + "user": "U111", + "text": "edited", + "ts": "123.420", + "thread_ts": "123.456", + }, + }, + }, + { + "event": { + "type": "message", + "subtype": "message_changed", + "channel": "C111", + "message": { + "type": "message", + "user": "U111", + "text": "edited", + "ts": "123.420", + "thread_ts": "123.456", + }, + "previous_message": { + "type": "message", + "user": "U111", + "text": "deleted", + "ts": "123.420", + "thread_ts": "123.420", + }, + }, + }, + { + "event": { + "type": "message", + "subtype": "message_deleted", + "channel": "C111", + "previous_message": { + "type": "message", + "user": "U111", + "text": "deleted", + "ts": "123.420", + "thread_ts": "123.456", + }, + }, + }, + { + "event": { + "type": "assistant_thread_started", + "assistant_thread": { + "user_id": "U123ABC456", + "context": { + "channel_id": "C123ABC456", + "team_id": "T123ABC456", + "enterprise_id": "E123ABC456", + }, + "channel_id": "D123ABC456", + "thread_ts": "123.456", + }, + "event_ts": "1715873754.429808", + }, + }, + { + "event": { + "type": "assistant_thread_context_changed", + "assistant_thread": { + "user_id": "U123ABC456", + "context": { + "channel_id": "C123ABC456", + "team_id": "T123ABC456", + "enterprise_id": "E123ABC456", + }, + "channel_id": "D123ABC456", + "thread_ts": "123.456", + }, + "event_ts": "17298244.022142", + }, + }, + { + "event": { + "type": "message", + "subtype": "message_changed", + "message": { + "text": "Chats from 2024-09-28", + "subtype": "assistant_app_thread", + "user": "U123456ABCD", + "type": "message", + "team": "T123456ABCD", + "thread_ts": "123.456", + "reply_count": 1, + "ts": "123.420", + }, + "channel": "D987654ABCD", + "hidden": True, + "ts": "123.420", + "event_ts": "123.420", + "channel_type": "im", + }, + }, + ] + + no_thread_ts_requests = [ + { + "event": { + "type": "reaction_added", + "user": "U111", + "reaction": "thumbsup", + "item": {"type": "message", "channel": "C111", "ts": "123.420"}, + }, + }, + { + "event": { + "type": "channel_created", + "channel": {"id": "C222", "name": "test", "created": 1678455198}, + }, + }, + { + "event": { + "type": "message", + "channel": "C111", + "user": "U111", + "text": "hello", + "ts": "123.420", + }, + }, + {}, + ] + slack_connect_authorizations = [ { "enterprise_id": "INSTALLED_ENTERPRISE_ID", @@ -337,6 +528,16 @@ def test_function_inputs_extraction(self): inputs = extract_function_inputs(req) assert inputs == {"customer_id": "Ux111"} + def test_extract_thread_ts(self): + for req in self.thread_ts_event_requests: + thread_ts = extract_thread_ts(req) + assert thread_ts == "123.456", f"Expected thread_ts for {req}" + + def test_extract_thread_ts_fail(self): + for req in self.no_thread_ts_requests: + thread_ts = extract_thread_ts(req) + assert thread_ts is None, f"Expected None for {req}" + def test_is_enterprise_install_extraction(self): for req in self.requests: should_be_false = extract_is_enterprise_install(req) diff --git a/tests/slack_bolt_async/middleware/attaching_agent_kwargs/__init__.py b/tests/slack_bolt_async/middleware/attaching_agent_kwargs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py b/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py new file mode 100644 index 000000000..55883e5f3 --- /dev/null +++ b/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py @@ -0,0 +1,69 @@ +import pytest +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.middleware.attaching_agent_kwargs.async_attaching_agent_kwargs import AsyncAttachingAgentKwargs +from slack_bolt.request.async_request import AsyncBoltRequest +from slack_bolt.response import BoltResponse +from tests.scenario_tests_async.test_events_assistant import ( + thread_started_event_body, + user_message_event_body, + channel_user_message_event_body, +) + + +async def next(): + return BoltResponse(status=200) + + +AGENT_KWARGS = ("say", "set_status", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") + + +class TestAsyncAttachingAgentKwargs: + @pytest.mark.asyncio + async def test_assistant_event_attaches_kwargs(self): + middleware = AsyncAttachingAgentKwargs() + req = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key in req.context, f"{key} should be set on context" + assert req.context["say"].thread_ts == "1726133698.626339" + + @pytest.mark.asyncio + async def test_user_message_event_attaches_kwargs(self): + middleware = AsyncAttachingAgentKwargs() + req = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key in req.context, f"{key} should be set on context" + assert req.context["say"].thread_ts == "1726133698.626339" + + @pytest.mark.asyncio + async def test_non_assistant_event_does_not_attach_kwargs(self): + middleware = AsyncAttachingAgentKwargs() + req = AsyncBoltRequest(body=channel_user_message_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key not in req.context, f"{key} should not be set on context" + + @pytest.mark.asyncio + async def test_non_event_does_not_attach_kwargs(self): + middleware = AsyncAttachingAgentKwargs() + req = AsyncBoltRequest(body="payload={}", headers={}) + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + for key in AGENT_KWARGS: + assert key not in req.context, f"{key} should not be set on context" From f1bc61f1827a16075ba137c78225e166719f75ec Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 19 Mar 2026 06:57:33 -0700 Subject: [PATCH 199/282] feat: add support for say_stream utility (#1462) Co-authored-by: Eden Zimbelman --- slack_bolt/__init__.py | 2 + slack_bolt/async_app.py | 2 + slack_bolt/context/async_context.py | 5 + slack_bolt/context/base_context.py | 1 + slack_bolt/context/context.py | 5 + slack_bolt/context/say_stream/__init__.py | 6 + .../context/say_stream/async_say_stream.py | 74 ++++++ slack_bolt/context/say_stream/say_stream.py | 74 ++++++ slack_bolt/kwargs_injection/args.py | 5 + slack_bolt/kwargs_injection/async_args.py | 5 + slack_bolt/kwargs_injection/async_utils.py | 1 + slack_bolt/kwargs_injection/utils.py | 1 + .../async_attaching_agent_kwargs.py | 12 + .../attaching_agent_kwargs.py | 12 + .../scenario_tests/test_events_say_stream.py | 238 +++++++++++++++++ .../test_events_say_stream.py | 250 ++++++++++++++++++ tests/slack_bolt/context/test_say_stream.py | 103 ++++++++ .../context/test_async_say_stream.py | 117 ++++++++ 18 files changed, 913 insertions(+) create mode 100644 slack_bolt/context/say_stream/__init__.py create mode 100644 slack_bolt/context/say_stream/async_say_stream.py create mode 100644 slack_bolt/context/say_stream/say_stream.py create mode 100644 tests/scenario_tests/test_events_say_stream.py create mode 100644 tests/scenario_tests_async/test_events_say_stream.py create mode 100644 tests/slack_bolt/context/test_say_stream.py create mode 100644 tests/slack_bolt_async/context/test_async_say_stream.py diff --git a/slack_bolt/__init__.py b/slack_bolt/__init__.py index 4e43252fd..dfe950bf2 100644 --- a/slack_bolt/__init__.py +++ b/slack_bolt/__init__.py @@ -14,6 +14,7 @@ from .context.fail import Fail from .context.respond import Respond from .context.say import Say +from .context.say_stream import SayStream from .kwargs_injection import Args from .listener import Listener from .listener_matcher import CustomListenerMatcher @@ -42,6 +43,7 @@ "Fail", "Respond", "Say", + "SayStream", "Args", "Listener", "CustomListenerMatcher", diff --git a/slack_bolt/async_app.py b/slack_bolt/async_app.py index fdf724d4c..f95d952aa 100644 --- a/slack_bolt/async_app.py +++ b/slack_bolt/async_app.py @@ -59,6 +59,7 @@ async def command(ack, body, respond): from .context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts from .context.get_thread_context.async_get_thread_context import AsyncGetThreadContext from .context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext +from .context.say_stream.async_say_stream import AsyncSayStream __all__ = [ "AsyncApp", @@ -66,6 +67,7 @@ async def command(ack, body, respond): "AsyncBoltContext", "AsyncRespond", "AsyncSay", + "AsyncSayStream", "AsyncListener", "AsyncCustomListenerMatcher", "AsyncBoltRequest", diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index 631f74a82..33f260d38 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -10,6 +10,7 @@ from slack_bolt.context.get_thread_context.async_get_thread_context import AsyncGetThreadContext from slack_bolt.context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext from slack_bolt.context.say.async_say import AsyncSay +from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream from slack_bolt.context.set_status.async_set_status import AsyncSetStatus from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts from slack_bolt.context.set_title.async_set_title import AsyncSetTitle @@ -203,6 +204,10 @@ def set_suggested_prompts(self) -> Optional[AsyncSetSuggestedPrompts]: def get_thread_context(self) -> Optional[AsyncGetThreadContext]: return self.get("get_thread_context") + @property + def say_stream(self) -> Optional[AsyncSayStream]: + return self.get("say_stream") + @property def save_thread_context(self) -> Optional[AsyncSaveThreadContext]: return self.get("save_thread_context") diff --git a/slack_bolt/context/base_context.py b/slack_bolt/context/base_context.py index 843d5ef60..502febcb8 100644 --- a/slack_bolt/context/base_context.py +++ b/slack_bolt/context/base_context.py @@ -38,6 +38,7 @@ class BaseContext(dict): "set_status", "set_title", "set_suggested_prompts", + "say_stream", ] # Note that these items are not copyable, so when you add new items to this list, # you must modify ThreadListenerRunner/AsyncioListenerRunner's _build_lazy_request method to pass the values. diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index 48df4ad32..6184d5083 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -10,6 +10,7 @@ from slack_bolt.context.respond import Respond from slack_bolt.context.save_thread_context import SaveThreadContext from slack_bolt.context.say import Say +from slack_bolt.context.say_stream import SayStream from slack_bolt.context.set_status import SetStatus from slack_bolt.context.set_suggested_prompts import SetSuggestedPrompts from slack_bolt.context.set_title import SetTitle @@ -204,6 +205,10 @@ def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") + @property + def say_stream(self) -> Optional[SayStream]: + return self.get("say_stream") + @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") diff --git a/slack_bolt/context/say_stream/__init__.py b/slack_bolt/context/say_stream/__init__.py new file mode 100644 index 000000000..86db7b1cc --- /dev/null +++ b/slack_bolt/context/say_stream/__init__.py @@ -0,0 +1,6 @@ +# Don't add async module imports here +from .say_stream import SayStream + +__all__ = [ + "SayStream", +] diff --git a/slack_bolt/context/say_stream/async_say_stream.py b/slack_bolt/context/say_stream/async_say_stream.py new file mode 100644 index 000000000..dc752d02a --- /dev/null +++ b/slack_bolt/context/say_stream/async_say_stream.py @@ -0,0 +1,74 @@ +import warnings +from typing import Optional + +from slack_sdk.web.async_client import AsyncWebClient +from slack_sdk.web.async_chat_stream import AsyncChatStream + +from slack_bolt.warning import ExperimentalWarning + + +class AsyncSayStream: + client: AsyncWebClient + channel: Optional[str] + recipient_team_id: Optional[str] + recipient_user_id: Optional[str] + thread_ts: Optional[str] + + def __init__( + self, + *, + client: AsyncWebClient, + channel: Optional[str] = None, + recipient_team_id: Optional[str] = None, + recipient_user_id: Optional[str] = None, + thread_ts: Optional[str] = None, + ): + self.client = client + self.channel = channel + self.recipient_team_id = recipient_team_id + self.recipient_user_id = recipient_user_id + self.thread_ts = thread_ts + + async def __call__( + self, + *, + buffer_size: Optional[int] = None, + channel: Optional[str] = None, + recipient_team_id: Optional[str] = None, + recipient_user_id: Optional[str] = None, + thread_ts: Optional[str] = None, + **kwargs, + ) -> AsyncChatStream: + """Starts a new chat stream with context. + + Warning: This is an experimental feature and may change in future versions. + """ + warnings.warn( + "say_stream is experimental and may change in future versions.", + category=ExperimentalWarning, + stacklevel=2, + ) + + channel = channel or self.channel + thread_ts = thread_ts or self.thread_ts + if channel is None: + raise ValueError("say_stream without channel here is unsupported") + if thread_ts is None: + raise ValueError("say_stream without thread_ts here is unsupported") + + if buffer_size is not None: + return await self.client.chat_stream( + buffer_size=buffer_size, + channel=channel, + recipient_team_id=recipient_team_id or self.recipient_team_id, + recipient_user_id=recipient_user_id or self.recipient_user_id, + thread_ts=thread_ts, + **kwargs, + ) + return await self.client.chat_stream( + channel=channel, + recipient_team_id=recipient_team_id or self.recipient_team_id, + recipient_user_id=recipient_user_id or self.recipient_user_id, + thread_ts=thread_ts, + **kwargs, + ) diff --git a/slack_bolt/context/say_stream/say_stream.py b/slack_bolt/context/say_stream/say_stream.py new file mode 100644 index 000000000..1e1d7985f --- /dev/null +++ b/slack_bolt/context/say_stream/say_stream.py @@ -0,0 +1,74 @@ +import warnings +from typing import Optional + +from slack_sdk import WebClient +from slack_sdk.web.chat_stream import ChatStream + +from slack_bolt.warning import ExperimentalWarning + + +class SayStream: + client: WebClient + channel: Optional[str] + recipient_team_id: Optional[str] + recipient_user_id: Optional[str] + thread_ts: Optional[str] + + def __init__( + self, + *, + client: WebClient, + channel: Optional[str] = None, + recipient_team_id: Optional[str] = None, + recipient_user_id: Optional[str] = None, + thread_ts: Optional[str] = None, + ): + self.client = client + self.channel = channel + self.recipient_team_id = recipient_team_id + self.recipient_user_id = recipient_user_id + self.thread_ts = thread_ts + + def __call__( + self, + *, + buffer_size: Optional[int] = None, + channel: Optional[str] = None, + recipient_team_id: Optional[str] = None, + recipient_user_id: Optional[str] = None, + thread_ts: Optional[str] = None, + **kwargs, + ) -> ChatStream: + """Starts a new chat stream with context. + + Warning: This is an experimental feature and may change in future versions. + """ + warnings.warn( + "say_stream is experimental and may change in future versions.", + category=ExperimentalWarning, + stacklevel=2, + ) + + channel = channel or self.channel + thread_ts = thread_ts or self.thread_ts + if channel is None: + raise ValueError("say_stream without channel here is unsupported") + if thread_ts is None: + raise ValueError("say_stream without thread_ts here is unsupported") + + if buffer_size is not None: + return self.client.chat_stream( + buffer_size=buffer_size, + channel=channel, + recipient_team_id=recipient_team_id or self.recipient_team_id, + recipient_user_id=recipient_user_id or self.recipient_user_id, + thread_ts=thread_ts, + **kwargs, + ) + return self.client.chat_stream( + channel=channel, + recipient_team_id=recipient_team_id or self.recipient_team_id, + recipient_user_id=recipient_user_id or self.recipient_user_id, + thread_ts=thread_ts, + **kwargs, + ) diff --git a/slack_bolt/kwargs_injection/args.py b/slack_bolt/kwargs_injection/args.py index 113e39c08..dfb242fd1 100644 --- a/slack_bolt/kwargs_injection/args.py +++ b/slack_bolt/kwargs_injection/args.py @@ -11,6 +11,7 @@ from slack_bolt.agent.agent import BoltAgent from slack_bolt.context.save_thread_context import SaveThreadContext from slack_bolt.context.say import Say +from slack_bolt.context.say_stream import SayStream from slack_bolt.context.set_status import SetStatus from slack_bolt.context.set_suggested_prompts import SetSuggestedPrompts from slack_bolt.context.set_title import SetTitle @@ -105,6 +106,8 @@ def handle_buttons(args): """`save_thread_context()` utility function for AI Agents & Assistants""" agent: Optional[BoltAgent] """`agent` listener argument for AI Agents & Assistants""" + say_stream: Optional[SayStream] + """`say_stream()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -139,6 +142,7 @@ def __init__( get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, agent: Optional[BoltAgent] = None, + say_stream: Optional[SayStream] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method @@ -173,6 +177,7 @@ def __init__( self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context self.agent = agent + self.say_stream = say_stream self.next: Callable[[], None] = next self.next_: Callable[[], None] = next diff --git a/slack_bolt/kwargs_injection/async_args.py b/slack_bolt/kwargs_injection/async_args.py index 1f1dde024..19719e900 100644 --- a/slack_bolt/kwargs_injection/async_args.py +++ b/slack_bolt/kwargs_injection/async_args.py @@ -10,6 +10,7 @@ from slack_bolt.context.get_thread_context.async_get_thread_context import AsyncGetThreadContext from slack_bolt.context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext from slack_bolt.context.say.async_say import AsyncSay +from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream from slack_bolt.context.set_status.async_set_status import AsyncSetStatus from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts from slack_bolt.context.set_title.async_set_title import AsyncSetTitle @@ -104,6 +105,8 @@ async def handle_buttons(args): """`save_thread_context()` utility function for AI Agents & Assistants""" agent: Optional[AsyncBoltAgent] """`agent` listener argument for AI Agents & Assistants""" + say_stream: Optional[AsyncSayStream] + """`say_stream()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], Awaitable[None]] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -138,6 +141,7 @@ def __init__( get_thread_context: Optional[AsyncGetThreadContext] = None, save_thread_context: Optional[AsyncSaveThreadContext] = None, agent: Optional[AsyncBoltAgent] = None, + say_stream: Optional[AsyncSayStream] = None, next: Callable[[], Awaitable[None]], **kwargs, # noqa ): @@ -169,6 +173,7 @@ def __init__( self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context self.agent = agent + self.say_stream = say_stream self.next: Callable[[], Awaitable[None]] = next self.next_: Callable[[], Awaitable[None]] = next diff --git a/slack_bolt/kwargs_injection/async_utils.py b/slack_bolt/kwargs_injection/async_utils.py index aa84b2d11..534fb6133 100644 --- a/slack_bolt/kwargs_injection/async_utils.py +++ b/slack_bolt/kwargs_injection/async_utils.py @@ -60,6 +60,7 @@ def build_async_required_kwargs( "set_suggested_prompts": request.context.set_suggested_prompts, "get_thread_context": request.context.get_thread_context, "save_thread_context": request.context.save_thread_context, + "say_stream": request.context.say_stream, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function diff --git a/slack_bolt/kwargs_injection/utils.py b/slack_bolt/kwargs_injection/utils.py index 5cd410a07..101e00099 100644 --- a/slack_bolt/kwargs_injection/utils.py +++ b/slack_bolt/kwargs_injection/utils.py @@ -59,6 +59,7 @@ def build_required_kwargs( "set_title": request.context.set_title, "set_suggested_prompts": request.context.set_suggested_prompts, "save_thread_context": request.context.save_thread_context, + "say_stream": request.context.say_stream, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function diff --git a/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py index 0b43c21ce..08851c1eb 100644 --- a/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py +++ b/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py @@ -2,6 +2,7 @@ from slack_bolt.context.assistant.async_assistant_utilities import AsyncAssistantUtilities from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore +from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream from slack_bolt.middleware.async_middleware import AsyncMiddleware from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.request.payload_utils import is_assistant_event, to_event @@ -36,4 +37,15 @@ async def async_process( req.context["set_suggested_prompts"] = assistant.set_suggested_prompts req.context["get_thread_context"] = assistant.get_thread_context req.context["save_thread_context"] = assistant.save_thread_context + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts = req.context.thread_ts or event.get("ts") + if req.context.channel_id and thread_ts: + req.context["say_stream"] = AsyncSayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts, + ) return await next() diff --git a/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py index 4963ea67d..38a62c0c8 100644 --- a/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py +++ b/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py @@ -2,6 +2,7 @@ from slack_bolt.context.assistant.assistant_utilities import AssistantUtilities from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore +from slack_bolt.context.say_stream.say_stream import SayStream from slack_bolt.middleware import Middleware from slack_bolt.request.payload_utils import is_assistant_event, to_event from slack_bolt.request.request import BoltRequest @@ -30,4 +31,15 @@ def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], Bo req.context["set_suggested_prompts"] = assistant.set_suggested_prompts req.context["get_thread_context"] = assistant.get_thread_context req.context["save_thread_context"] = assistant.save_thread_context + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts = req.context.thread_ts or event.get("ts") + if req.context.channel_id and thread_ts: + req.context["say_stream"] = SayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts, + ) return next() diff --git a/tests/scenario_tests/test_events_say_stream.py b/tests/scenario_tests/test_events_say_stream.py new file mode 100644 index 000000000..75b0c612c --- /dev/null +++ b/tests/scenario_tests/test_events_say_stream.py @@ -0,0 +1,238 @@ +import json +import time +from urllib.parse import quote + +from slack_sdk.web import WebClient + +from slack_bolt import App, BoltRequest, BoltContext +from slack_bolt.context.say_stream.say_stream import SayStream +from slack_bolt.middleware.assistant import Assistant +from tests.mock_web_api_server import ( + setup_mock_web_api_server, + cleanup_mock_web_api_server, +) +from tests.scenario_tests.test_app import app_mention_event_body +from tests.scenario_tests.test_events_assistant import ( + thread_started_event_body, + user_message_event_body as threaded_user_message_event_body, +) +from tests.scenario_tests.test_message_bot import bot_message_event_payload, user_message_event_payload +from tests.scenario_tests.test_view_submission import body as view_submission_body +from tests.utils import remove_os_env_temporarily, restore_os_env + + +def assert_target_called(called: dict, timeout: float = 1.0): + deadline = time.time() + timeout + while called["value"] is not True and time.time() < deadline: + time.sleep(0.1) + assert called["value"] is True + + +class TestEventsSayStream: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = WebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + def setup_method(self): + self.old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + restore_os_env(self.old_os_env) + + def test_say_stream_injected_for_app_mention(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("app_mention") + def handle_mention(say_stream: SayStream, context: BoltContext): + assert say_stream is not None + assert isinstance(say_stream, SayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "C111" + assert say_stream.thread_ts == "1595926230.009600" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = BoltRequest(body=app_mention_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_say_stream_with_org_level_install(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("app_mention") + def handle_mention(say_stream: SayStream, context: BoltContext): + assert context.team_id is None + assert context.enterprise_id == "E111" + assert say_stream is not None + assert isinstance(say_stream, SayStream) + assert say_stream.recipient_team_id == "E111" + called["value"] = True + + request = BoltRequest(body=org_app_mention_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_say_stream_injected_for_threaded_message(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.event("message") + def handle_message(say_stream: SayStream, context: BoltContext): + assert say_stream is not None + assert isinstance(say_stream, SayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "D111" + assert say_stream.thread_ts == "1726133698.626339" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = BoltRequest(body=threaded_user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_say_stream_in_user_message(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.message("") + def handle_user_message(say_stream: SayStream, context: BoltContext): + assert say_stream is not None + assert isinstance(say_stream, SayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "C111" + assert say_stream.thread_ts == "1610261659.001400" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = BoltRequest(body=user_message_event_payload, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_say_stream_in_bot_message(self): + app = App(client=self.web_client) + called = {"value": False} + + @app.message("") + def handle_bot_message(say_stream: SayStream, context: BoltContext): + assert say_stream is not None + assert isinstance(say_stream, SayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "C111" + assert say_stream.thread_ts == "1610261539.000900" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = BoltRequest(body=bot_message_event_payload, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_say_stream_in_assistant_thread_started(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} + + @assistant.thread_started + def start_thread(say_stream: SayStream, context: BoltContext): + assert say_stream is not None + assert isinstance(say_stream, SayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "D111" + assert say_stream.thread_ts == "1726133698.626339" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + app.assistant(assistant) + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_say_stream_in_assistant_user_message(self): + app = App(client=self.web_client) + assistant = Assistant() + called = {"value": False} + + @assistant.user_message + def handle_user_message(say_stream: SayStream, context: BoltContext): + assert say_stream is not None + assert isinstance(say_stream, SayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "D111" + assert say_stream.thread_ts == "1726133698.626339" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + app.assistant(assistant) + + request = BoltRequest(body=threaded_user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + def test_say_stream_is_none_for_view_submission(self): + app = App(client=self.web_client, request_verification_enabled=False) + called = {"value": False} + + @app.view("view-id") + def handle_view(ack, say_stream, context: BoltContext): + ack() + assert say_stream is None + assert context.say_stream is None + called["value"] = True + + request = BoltRequest( + body=f"payload={quote(json.dumps(view_submission_body))}", + ) + response = app.dispatch(request) + assert response.status == 200 + assert_target_called(called) + + +org_app_mention_event_body = { + "token": "verification_token", + "team_id": "T111", + "enterprise_id": "E111", + "api_app_id": "A111", + "event": { + "client_msg_id": "9cbd4c5b-7ddf-4ede-b479-ad21fca66d63", + "type": "app_mention", + "text": "<@W111> Hi there!", + "user": "W222", + "ts": "1595926230.009600", + "team": "T111", + "channel": "C111", + "event_ts": "1595926230.009600", + }, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1595926230, + "authorizations": [ + { + "enterprise_id": "E111", + "team_id": None, + "user_id": "W111", + "is_bot": True, + "is_enterprise_install": True, + } + ], + "is_ext_shared_channel": False, +} diff --git a/tests/scenario_tests_async/test_events_say_stream.py b/tests/scenario_tests_async/test_events_say_stream.py new file mode 100644 index 000000000..c24bc7bfc --- /dev/null +++ b/tests/scenario_tests_async/test_events_say_stream.py @@ -0,0 +1,250 @@ +import asyncio +import json +import time +from urllib.parse import quote + +import pytest +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.app.async_app import AsyncApp +from slack_bolt.async_app import AsyncAssistant +from slack_bolt.context.async_context import AsyncBoltContext +from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream +from slack_bolt.request.async_request import AsyncBoltRequest +from tests.mock_web_api_server import ( + cleanup_mock_web_api_server_async, + setup_mock_web_api_server_async, +) +from tests.scenario_tests_async.test_app import app_mention_event_body +from tests.scenario_tests_async.test_events_assistant import user_message_event_body as threaded_user_message_event_body +from tests.scenario_tests_async.test_events_assistant import thread_started_event_body, user_message_event_body +from tests.scenario_tests_async.test_message_bot import bot_message_event_payload, user_message_event_payload +from tests.scenario_tests_async.test_view_submission import body as view_submission_body +from tests.utils import remove_os_env_temporarily, restore_os_env + + +async def assert_target_called(called: dict, timeout: float = 0.5): + deadline = time.time() + timeout + while called["value"] is not True and time.time() < deadline: + await asyncio.sleep(0.1) + assert called["value"] is True + + +class TestAsyncEventsSayStream: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = AsyncWebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) + try: + yield + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) + + @pytest.mark.asyncio + async def test_say_stream_injected_for_app_mention(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("app_mention") + async def handle_mention(say_stream: AsyncSayStream, context: AsyncBoltContext): + assert say_stream is not None + assert isinstance(say_stream, AsyncSayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "C111" + assert say_stream.thread_ts == "1595926230.009600" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = AsyncBoltRequest(body=app_mention_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_say_stream_with_org_level_install(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("app_mention") + async def handle_mention(say_stream: AsyncSayStream, context: AsyncBoltContext): + assert context.team_id is None + assert context.enterprise_id == "E111" + assert say_stream is not None + assert isinstance(say_stream, AsyncSayStream) + assert say_stream.recipient_team_id == "E111" + called["value"] = True + + request = AsyncBoltRequest(body=org_app_mention_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_say_stream_injected_for_threaded_message(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.event("message") + async def handle_message(say_stream: AsyncSayStream, context: AsyncBoltContext): + assert say_stream is not None + assert isinstance(say_stream, AsyncSayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "D111" + assert say_stream.thread_ts == "1726133698.626339" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = AsyncBoltRequest(body=threaded_user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_say_stream_in_user_message(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.message("") + async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltContext): + assert say_stream is not None + assert isinstance(say_stream, AsyncSayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "C111" + assert say_stream.thread_ts == "1610261659.001400" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = AsyncBoltRequest(body=user_message_event_payload, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_say_stream_in_bot_message(self): + app = AsyncApp(client=self.web_client) + called = {"value": False} + + @app.message("") + async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltContext): + assert say_stream is not None + assert isinstance(say_stream, AsyncSayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "C111" + assert say_stream.thread_ts == "1610261539.000900" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + request = AsyncBoltRequest(body=bot_message_event_payload, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_say_stream_in_assistant_thread_started(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} + + @assistant.thread_started + async def start_thread(say_stream: AsyncSayStream, context: AsyncBoltContext): + assert say_stream is not None + assert isinstance(say_stream, AsyncSayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "D111" + assert say_stream.thread_ts == "1726133698.626339" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + app.assistant(assistant) + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_say_stream_in_assistant_user_message(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + called = {"value": False} + + @assistant.user_message + async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltContext): + assert say_stream is not None + assert isinstance(say_stream, AsyncSayStream) + assert say_stream == context.say_stream + assert say_stream.channel == "D111" + assert say_stream.thread_ts == "1726133698.626339" + assert say_stream.recipient_team_id == context.team_id + assert say_stream.recipient_user_id == context.user_id + called["value"] = True + + app.assistant(assistant) + + request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + @pytest.mark.asyncio + async def test_say_stream_is_none_for_view_submission(self): + app = AsyncApp(client=self.web_client, request_verification_enabled=False) + called = {"value": False} + + @app.view("view-id") + async def handle_view(ack, say_stream, context: AsyncBoltContext): + await ack() + assert say_stream is None + assert context.say_stream is None + called["value"] = True + + request = AsyncBoltRequest( + body=f"payload={quote(json.dumps(view_submission_body))}", + ) + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called(called) + + +org_app_mention_event_body = { + "token": "verification_token", + "team_id": "T111", + "enterprise_id": "E111", + "api_app_id": "A111", + "event": { + "client_msg_id": "9cbd4c5b-7ddf-4ede-b479-ad21fca66d63", + "type": "app_mention", + "text": "<@W111> Hi there!", + "user": "W222", + "ts": "1595926230.009600", + "team": "T111", + "channel": "C111", + "event_ts": "1595926230.009600", + }, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1595926230, + "authorizations": [ + { + "enterprise_id": "E111", + "team_id": None, + "user_id": "W111", + "is_bot": True, + "is_enterprise_install": True, + } + ], + "is_ext_shared_channel": False, +} diff --git a/tests/slack_bolt/context/test_say_stream.py b/tests/slack_bolt/context/test_say_stream.py new file mode 100644 index 000000000..c8f4c3a31 --- /dev/null +++ b/tests/slack_bolt/context/test_say_stream.py @@ -0,0 +1,103 @@ +import pytest +from slack_sdk import WebClient + +from slack_bolt.context.say_stream.say_stream import SayStream +from slack_bolt.warning import ExperimentalWarning +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server + + +class TestSayStream: + default_chat_stream_buffer_size = WebClient.chat_stream.__kwdefaults__["buffer_size"] + + def setup_method(self): + setup_mock_web_api_server(self) + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + + def test_missing_channel_raises(self): + say_stream = SayStream(client=self.web_client, channel=None, thread_ts="111.222") + with pytest.warns(ExperimentalWarning): + with pytest.raises(ValueError, match="channel"): + say_stream() + + def test_missing_thread_ts_raises(self): + say_stream = SayStream(client=self.web_client, channel="C111", thread_ts=None) + with pytest.warns(ExperimentalWarning): + with pytest.raises(ValueError, match="thread_ts"): + say_stream() + + def test_default_params(self): + say_stream = SayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = say_stream() + + assert stream._buffer_size == self.default_chat_stream_buffer_size + assert stream._stream_args == { + "channel": "C111", + "thread_ts": "111.222", + "recipient_team_id": "T111", + "recipient_user_id": "U111", + "task_display_mode": None, + } + + def test_parameter_overrides(self): + say_stream = SayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") + + assert stream._buffer_size == self.default_chat_stream_buffer_size + assert stream._stream_args == { + "channel": "C222", + "thread_ts": "333.444", + "recipient_team_id": "T222", + "recipient_user_id": "U222", + "task_display_mode": None, + } + + def test_buffer_size_overrides(self): + say_stream = SayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = say_stream( + buffer_size=100, + channel="C222", + thread_ts="333.444", + recipient_team_id="T222", + recipient_user_id="U222", + ) + + assert stream._buffer_size == 100 + assert stream._stream_args == { + "channel": "C222", + "thread_ts": "333.444", + "recipient_team_id": "T222", + "recipient_user_id": "U222", + "task_display_mode": None, + } + + def test_experimental_warning(self): + say_stream = SayStream( + client=self.web_client, + channel="C111", + thread_ts="111.222", + ) + with pytest.warns(ExperimentalWarning, match="say_stream is experimental"): + say_stream() diff --git a/tests/slack_bolt_async/context/test_async_say_stream.py b/tests/slack_bolt_async/context/test_async_say_stream.py new file mode 100644 index 000000000..fbc4c5c7e --- /dev/null +++ b/tests/slack_bolt_async/context/test_async_say_stream.py @@ -0,0 +1,117 @@ +import pytest +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream +from slack_bolt.warning import ExperimentalWarning +from tests.mock_web_api_server import ( + cleanup_mock_web_api_server, + setup_mock_web_api_server, +) +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestAsyncSayStream: + default_chat_stream_buffer_size = AsyncWebClient.chat_stream.__kwdefaults__["buffer_size"] + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + try: + self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) + yield # run the test here + finally: + cleanup_mock_web_api_server(self) + restore_os_env(old_os_env) + + @pytest.mark.asyncio + async def test_missing_channel_raises(self): + say_stream = AsyncSayStream(client=self.web_client, channel=None, thread_ts="111.222") + with pytest.warns(ExperimentalWarning): + with pytest.raises(ValueError, match="channel"): + await say_stream() + + @pytest.mark.asyncio + async def test_missing_thread_ts_raises(self): + say_stream = AsyncSayStream(client=self.web_client, channel="C111", thread_ts=None) + with pytest.warns(ExperimentalWarning): + with pytest.raises(ValueError, match="thread_ts"): + await say_stream() + + @pytest.mark.asyncio + async def test_default_params(self): + say_stream = AsyncSayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = await say_stream() + + assert stream._buffer_size == self.default_chat_stream_buffer_size + assert stream._stream_args == { + "channel": "C111", + "thread_ts": "111.222", + "recipient_team_id": "T111", + "recipient_user_id": "U111", + "task_display_mode": None, + } + + @pytest.mark.asyncio + async def test_parameter_overrides(self): + say_stream = AsyncSayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = await say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") + + assert stream._buffer_size == self.default_chat_stream_buffer_size + assert stream._stream_args == { + "channel": "C222", + "thread_ts": "333.444", + "recipient_team_id": "T222", + "recipient_user_id": "U222", + "task_display_mode": None, + } + + @pytest.mark.asyncio + async def test_buffer_size_overrides(self): + say_stream = AsyncSayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = await say_stream( + buffer_size=100, + channel="C222", + thread_ts="333.444", + recipient_team_id="T222", + recipient_user_id="U222", + ) + + assert stream._buffer_size == 100 + assert stream._stream_args == { + "channel": "C222", + "thread_ts": "333.444", + "recipient_team_id": "T222", + "recipient_user_id": "U222", + "task_display_mode": None, + } + + @pytest.mark.asyncio + async def test_experimental_warning(self): + say_stream = AsyncSayStream( + client=self.web_client, + channel="C111", + thread_ts="111.222", + ) + with pytest.warns(ExperimentalWarning, match="say_stream is experimental"): + await say_stream() From 7aa415ff63b9ec4b5a241cb5886fe4ff9f8588eb Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 19 Mar 2026 11:44:20 -0700 Subject: [PATCH 200/282] fix: improve the robustness of the payload extract logic (#1464) --- slack_bolt/request/internals.py | 42 +++++++++++----------- tests/slack_bolt/request/test_internals.py | 25 +++++++++++++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/slack_bolt/request/internals.py b/slack_bolt/request/internals.py index 466f5daaf..15d1e7367 100644 --- a/slack_bolt/request/internals.py +++ b/slack_bolt/request/internals.py @@ -65,10 +65,10 @@ def extract_enterprise_id(payload: Dict[str, Any]) -> Optional[str]: return extract_enterprise_id(payload["authorizations"][0]) if "enterprise_id" in payload: return payload.get("enterprise_id") - if payload.get("team") is not None and "enterprise_id" in payload["team"]: + if isinstance(payload.get("team"), dict) and "enterprise_id" in payload["team"]: # In the case where the type is view_submission return payload["team"].get("enterprise_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_enterprise_id(payload["event"]) return None @@ -88,13 +88,13 @@ def extract_actor_enterprise_id(payload: Dict[str, Any]) -> Optional[str]: def extract_team_id(payload: Dict[str, Any]) -> Optional[str]: - app_installed_team_id = payload.get("view", {}).get("app_installed_team_id") - if app_installed_team_id is not None: + view = payload.get("view") + if isinstance(view, dict) and view.get("app_installed_team_id") is not None: # view_submission payloads can have `view.app_installed_team_id` when a modal view that was opened # in a different workspace via some operations inside a Slack Connect channel. # Note that the same for enterprise_id does not exist. When you need to know the enterprise_id as well, # you have to run some query toward your InstallationStore to know the org where the team_id belongs to. - return app_installed_team_id + return view["app_installed_team_id"] if payload.get("team") is not None: # With org-wide installations, payload.team in interactivity payloads can be None # You need to extract either payload.user.team_id or payload.view.team_id as below @@ -109,12 +109,12 @@ def extract_team_id(payload: Dict[str, Any]) -> Optional[str]: return extract_team_id(payload["authorizations"][0]) if "team_id" in payload: return payload.get("team_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_team_id(payload["event"]) - if payload.get("user") is not None: + if isinstance(payload.get("user"), dict): return payload["user"]["team_id"] - if payload.get("view") is not None: - return payload.get("view", {})["team_id"] + if isinstance(payload.get("view"), dict): + return payload["view"]["team_id"] return None @@ -169,12 +169,12 @@ def extract_user_id(payload: Dict[str, Any]) -> Optional[str]: return user.get("id") if "user_id" in payload: return payload.get("user_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_user_id(payload["event"]) - if payload.get("message") is not None: + if isinstance(payload.get("message"), dict): # message_changed: body["event"]["message"] return extract_user_id(payload["message"]) - if payload.get("previous_message") is not None: + if isinstance(payload.get("previous_message"), dict): # message_deleted: body["event"]["previous_message"] return extract_user_id(payload["previous_message"]) return None @@ -202,12 +202,12 @@ def extract_channel_id(payload: Dict[str, Any]) -> Optional[str]: return channel.get("id") if "channel_id" in payload: return payload.get("channel_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_channel_id(payload["event"]) - if payload.get("item") is not None: + if isinstance(payload.get("item"), dict): # reaction_added: body["event"]["item"] return extract_channel_id(payload["item"]) - if payload.get("assistant_thread") is not None: + if isinstance(payload.get("assistant_thread"), dict): # assistant_thread_started return extract_channel_id(payload["assistant_thread"]) return None @@ -217,7 +217,7 @@ def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]: thread_ts = payload.get("thread_ts") if thread_ts is not None: return thread_ts - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_thread_ts(payload["event"]) if isinstance(payload.get("assistant_thread"), dict): return extract_thread_ts(payload["assistant_thread"]) @@ -231,9 +231,9 @@ def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]: def extract_function_execution_id(payload: Dict[str, Any]) -> Optional[str]: if payload.get("function_execution_id") is not None: return payload.get("function_execution_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_function_execution_id(payload["event"]) - if payload.get("function_data") is not None: + if isinstance(payload.get("function_data"), dict): return payload["function_data"].get("execution_id") return None @@ -241,15 +241,15 @@ def extract_function_execution_id(payload: Dict[str, Any]) -> Optional[str]: def extract_function_bot_access_token(payload: Dict[str, Any]) -> Optional[str]: if payload.get("bot_access_token") is not None: return payload.get("bot_access_token") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return payload["event"].get("bot_access_token") return None def extract_function_inputs(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]: - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return payload["event"].get("inputs") - if payload.get("function_data") is not None: + if isinstance(payload.get("function_data"), dict): return payload["function_data"].get("inputs") return None diff --git a/tests/slack_bolt/request/test_internals.py b/tests/slack_bolt/request/test_internals.py index 0b267e3de..8cccf0431 100644 --- a/tests/slack_bolt/request/test_internals.py +++ b/tests/slack_bolt/request/test_internals.py @@ -1248,3 +1248,28 @@ def test_slack_connect_patterns(self): assert extract_actor_enterprise_id(request) == actor_enterprise_id assert extract_actor_team_id(request) == actor_team_id assert extract_actor_user_id(request) == actor_user_id + + def test_extraction_functions_invalid_dict_keys(self): + invalid_payloads = { + "event": {"event": "some_event_type"}, + "user": {"user": "U12345"}, + "team": {"team": "T12345"}, + "view": {"view": "V12345"}, + "message": {"message": "some text"}, + "item": {"item": "item_id"}, + "function_data": {"function_data": "fd_123"}, + "assistant_thread": {"assistant_thread": "at_123"}, + "previous_message": {"previous_message": "old_msg"}, + } + + for _, payload in invalid_payloads.items(): + # We only verify no TypeError/AttributeError is raised and that functions which + # would try to subscript the string value return None instead of crashing. + extract_enterprise_id(payload) + extract_team_id(payload) + extract_user_id(payload) + extract_channel_id(payload) + extract_thread_ts(payload) + extract_function_execution_id(payload) + extract_function_bot_access_token(payload) + extract_function_inputs(payload) From ba7df02bdac421d9a23ebe984cc51ab33da6b693 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 19 Mar 2026 13:01:15 -0700 Subject: [PATCH 201/282] feat: surface the set_status argument to listeners if required event details are available (#1465) --- .../context/assistant/assistant_utilities.py | 8 + .../assistant/async_assistant_utilities.py | 8 + .../async_attaching_agent_kwargs.py | 7 +- .../attaching_agent_kwargs.py | 7 +- ...est_events_assistant_without_middleware.py | 6 +- .../scenario_tests/test_events_set_status.py | 171 ++++++++++++++++ ...est_events_assistant_without_middleware.py | 6 +- .../test_events_set_status.py | 183 ++++++++++++++++++ .../test_attaching_agent_kwargs.py | 18 +- .../test_async_attaching_agent_kwargs.py | 18 +- 10 files changed, 414 insertions(+), 18 deletions(-) create mode 100644 tests/scenario_tests/test_events_set_status.py create mode 100644 tests/scenario_tests_async/test_events_set_status.py diff --git a/slack_bolt/context/assistant/assistant_utilities.py b/slack_bolt/context/assistant/assistant_utilities.py index 53500efdb..42f05c94b 100644 --- a/slack_bolt/context/assistant/assistant_utilities.py +++ b/slack_bolt/context/assistant/assistant_utilities.py @@ -1,3 +1,4 @@ +import warnings from typing import Optional from slack_sdk.web import WebClient @@ -51,6 +52,13 @@ def is_valid(self) -> bool: @property def set_status(self) -> SetStatus: + warnings.warn( + "AssistantUtilities.set_status is deprecated. " + "Use the set_status argument directly in your listener function " + "or access it via context.set_status instead.", + DeprecationWarning, + stacklevel=2, + ) return SetStatus(self.client, self.channel_id, self.thread_ts) @property diff --git a/slack_bolt/context/assistant/async_assistant_utilities.py b/slack_bolt/context/assistant/async_assistant_utilities.py index 5a7324e99..b40b2619c 100644 --- a/slack_bolt/context/assistant/async_assistant_utilities.py +++ b/slack_bolt/context/assistant/async_assistant_utilities.py @@ -1,3 +1,4 @@ +import warnings from typing import Optional from slack_sdk.web.async_client import AsyncWebClient @@ -54,6 +55,13 @@ def is_valid(self) -> bool: @property def set_status(self) -> AsyncSetStatus: + warnings.warn( + "AsyncAssistantUtilities.set_status is deprecated. " + "Use the set_status argument directly in your listener function " + "or access it via context.set_status instead.", + DeprecationWarning, + stacklevel=2, + ) return AsyncSetStatus(self.client, self.channel_id, self.thread_ts) @property diff --git a/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py index 08851c1eb..82f1a7671 100644 --- a/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py +++ b/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py @@ -3,6 +3,7 @@ from slack_bolt.context.assistant.async_assistant_utilities import AsyncAssistantUtilities from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream +from slack_bolt.context.set_status.async_set_status import AsyncSetStatus from slack_bolt.middleware.async_middleware import AsyncMiddleware from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.request.payload_utils import is_assistant_event, to_event @@ -32,7 +33,6 @@ async def async_process( thread_context_store=self.thread_context_store, ) req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status req.context["set_title"] = assistant.set_title req.context["set_suggested_prompts"] = assistant.set_suggested_prompts req.context["get_thread_context"] = assistant.get_thread_context @@ -41,6 +41,11 @@ async def async_process( # TODO: in the future we might want to introduce a "proper" extract_ts utility thread_ts = req.context.thread_ts or event.get("ts") if req.context.channel_id and thread_ts: + req.context["set_status"] = AsyncSetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts, + ) req.context["say_stream"] = AsyncSayStream( client=req.context.client, channel=req.context.channel_id, diff --git a/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py index 38a62c0c8..70f41d561 100644 --- a/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py +++ b/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py @@ -3,6 +3,7 @@ from slack_bolt.context.assistant.assistant_utilities import AssistantUtilities from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore from slack_bolt.context.say_stream.say_stream import SayStream +from slack_bolt.context.set_status.set_status import SetStatus from slack_bolt.middleware import Middleware from slack_bolt.request.payload_utils import is_assistant_event, to_event from slack_bolt.request.request import BoltRequest @@ -26,7 +27,6 @@ def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], Bo thread_context_store=self.thread_context_store, ) req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status req.context["set_title"] = assistant.set_title req.context["set_suggested_prompts"] = assistant.set_suggested_prompts req.context["get_thread_context"] = assistant.get_thread_context @@ -35,6 +35,11 @@ def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], Bo # TODO: in the future we might want to introduce a "proper" extract_ts utility thread_ts = req.context.thread_ts or event.get("ts") if req.context.channel_id and thread_ts: + req.context["set_status"] = SetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts, + ) req.context["say_stream"] = SayStream( client=req.context.client, channel=req.context.channel_id, diff --git a/tests/scenario_tests/test_events_assistant_without_middleware.py b/tests/scenario_tests/test_events_assistant_without_middleware.py index 36d86c43a..6a9381a33 100644 --- a/tests/scenario_tests/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests/test_events_assistant_without_middleware.py @@ -180,7 +180,7 @@ def handle_message_event( ): assert context.thread_ts == "1726133698.626339" assert say.thread_ts == None - assert set_status is None + assert set_status is not None assert set_title is None assert set_suggested_prompts is None assert get_thread_context is None @@ -208,7 +208,7 @@ def handle_message_event( ): assert context.thread_ts == "1726133698.626339" assert say.thread_ts == None - assert set_status is None + assert set_status is not None assert set_title is None assert set_suggested_prompts is None assert get_thread_context is None @@ -236,7 +236,7 @@ def handle_message_event( ): assert context.thread_ts == "1726133698.626339" assert say.thread_ts == None - assert set_status is None + assert set_status is not None assert set_title is None assert set_suggested_prompts is None assert get_thread_context is None diff --git a/tests/scenario_tests/test_events_set_status.py b/tests/scenario_tests/test_events_set_status.py new file mode 100644 index 000000000..2dbdd38b8 --- /dev/null +++ b/tests/scenario_tests/test_events_set_status.py @@ -0,0 +1,171 @@ +import json +from threading import Event +from urllib.parse import quote + +from slack_sdk.web import WebClient + +from slack_bolt import App, BoltContext, BoltRequest +from slack_bolt.context.set_status.set_status import SetStatus +from slack_bolt.middleware.assistant import Assistant +from tests.mock_web_api_server import ( + assert_auth_test_count, + assert_received_request_count, + cleanup_mock_web_api_server, + setup_mock_web_api_server, +) +from tests.scenario_tests.test_app import app_mention_event_body +from tests.scenario_tests.test_events_assistant import thread_started_event_body +from tests.scenario_tests.test_events_assistant import user_message_event_body as threaded_user_message_event_body +from tests.scenario_tests.test_message_bot import bot_message_event_payload, user_message_event_payload +from tests.scenario_tests.test_view_submission import body as view_submission_body +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestEventsSetStatus: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = WebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + def setup_method(self): + self.old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server(self) + + def teardown_method(self): + cleanup_mock_web_api_server(self) + restore_os_env(self.old_os_env) + + def test_set_status_injected_for_app_mention(self): + app = App(client=self.web_client) + + @app.event("app_mention") + def handle_mention(set_status: SetStatus, context: BoltContext): + assert set_status is not None + assert isinstance(set_status, SetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "C111" + assert set_status.thread_ts == "1595926230.009600" + set_status(status="Thinking...") + + request = BoltRequest(body=app_mention_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + assert_received_request_count(self, path="/assistant.threads.setStatus", min_count=1) + + def test_set_status_injected_for_threaded_message(self): + app = App(client=self.web_client) + + @app.event("message") + def handle_message(set_status: SetStatus, context: BoltContext): + assert set_status is not None + assert isinstance(set_status, SetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "D111" + assert set_status.thread_ts == "1726133698.626339" + set_status(status="Thinking...") + + request = BoltRequest(body=threaded_user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + assert_received_request_count(self, path="/assistant.threads.setStatus", min_count=1) + + def test_set_status_in_user_message(self): + app = App(client=self.web_client) + + @app.message("") + def handle_user_message(set_status: SetStatus, context: BoltContext): + assert set_status is not None + assert isinstance(set_status, SetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "C111" + assert set_status.thread_ts == "1610261659.001400" + set_status(status="Thinking...") + + request = BoltRequest(body=user_message_event_payload, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + assert_received_request_count(self, path="/assistant.threads.setStatus", min_count=1) + + def test_set_status_in_bot_message(self): + app = App(client=self.web_client) + + @app.message("") + def handle_bot_message(set_status: SetStatus, context: BoltContext): + assert set_status is not None + assert isinstance(set_status, SetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "C111" + assert set_status.thread_ts == "1610261539.000900" + set_status(status="Thinking...") + + request = BoltRequest(body=bot_message_event_payload, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + assert_received_request_count(self, path="/assistant.threads.setStatus", min_count=1) + + def test_set_status_in_assistant_thread_started(self): + app = App(client=self.web_client) + assistant = Assistant() + + @assistant.thread_started + def start_thread(set_status: SetStatus, context: BoltContext): + assert set_status is not None + assert isinstance(set_status, SetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "D111" + assert set_status.thread_ts == "1726133698.626339" + set_status(status="Thinking...") + + app.assistant(assistant) + + request = BoltRequest(body=thread_started_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + assert_received_request_count(self, path="/assistant.threads.setStatus", min_count=1) + + def test_set_status_in_assistant_user_message(self): + app = App(client=self.web_client) + assistant = Assistant() + + @assistant.user_message + def handle_user_message(set_status: SetStatus, context: BoltContext): + assert set_status is not None + assert isinstance(set_status, SetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "D111" + assert set_status.thread_ts == "1726133698.626339" + set_status(status="Thinking...") + + app.assistant(assistant) + + request = BoltRequest(body=threaded_user_message_event_body, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + assert_received_request_count(self, path="/assistant.threads.setStatus", min_count=1) + + def test_set_status_is_none_for_view_submission(self): + app = App(client=self.web_client, request_verification_enabled=False) + listener_called = Event() + + @app.view("view-id") + def handle_view(ack, set_status, context: BoltContext): + ack() + assert set_status is None + assert context.set_status is None + listener_called.set() + + request = BoltRequest( + body=f"payload={quote(json.dumps(view_submission_body))}", + ) + response = app.dispatch(request) + assert response.status == 200 + assert_auth_test_count(self, 1) + assert listener_called.is_set() diff --git a/tests/scenario_tests_async/test_events_assistant_without_middleware.py b/tests/scenario_tests_async/test_events_assistant_without_middleware.py index be6c2b166..916dfd467 100644 --- a/tests/scenario_tests_async/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests_async/test_events_assistant_without_middleware.py @@ -197,7 +197,7 @@ async def handle_message_event( ): assert context.thread_ts == "1726133698.626339" assert say.thread_ts == None - assert set_status is None + assert set_status is not None assert set_title is None assert set_suggested_prompts is None assert get_thread_context is None @@ -226,7 +226,7 @@ async def handle_message_event( ): assert context.thread_ts == "1726133698.626339" assert say.thread_ts == None - assert set_status is None + assert set_status is not None assert set_title is None assert set_suggested_prompts is None assert get_thread_context is None @@ -255,7 +255,7 @@ async def handle_message_event( ): assert context.thread_ts == "1726133698.626339" assert say.thread_ts == None - assert set_status is None + assert set_status is not None assert set_title is None assert set_suggested_prompts is None assert get_thread_context is None diff --git a/tests/scenario_tests_async/test_events_set_status.py b/tests/scenario_tests_async/test_events_set_status.py new file mode 100644 index 000000000..0e5be3349 --- /dev/null +++ b/tests/scenario_tests_async/test_events_set_status.py @@ -0,0 +1,183 @@ +import asyncio +import json +from urllib.parse import quote + +import pytest +from slack_sdk.web.async_client import AsyncWebClient + +from slack_bolt.app.async_app import AsyncApp +from slack_bolt.async_app import AsyncAssistant +from slack_bolt.context.async_context import AsyncBoltContext +from slack_bolt.context.set_status.async_set_status import AsyncSetStatus +from slack_bolt.request.async_request import AsyncBoltRequest +from tests.mock_web_api_server import ( + assert_auth_test_count_async, + assert_received_request_count_async, + cleanup_mock_web_api_server_async, + setup_mock_web_api_server_async, +) +from tests.scenario_tests_async.test_app import app_mention_event_body +from tests.scenario_tests_async.test_events_assistant import thread_started_event_body +from tests.scenario_tests_async.test_events_assistant import user_message_event_body as threaded_user_message_event_body +from tests.scenario_tests_async.test_message_bot import bot_message_event_payload, user_message_event_payload +from tests.scenario_tests_async.test_view_submission import body as view_submission_body +from tests.utils import remove_os_env_temporarily, restore_os_env + + +class TestAsyncEventsSetStatus: + valid_token = "xoxb-valid" + mock_api_server_base_url = "http://localhost:8888" + web_client = AsyncWebClient( + token=valid_token, + base_url=mock_api_server_base_url, + ) + + @pytest.fixture(scope="function", autouse=True) + def setup_teardown(self): + old_os_env = remove_os_env_temporarily() + setup_mock_web_api_server_async(self) + try: + yield + finally: + cleanup_mock_web_api_server_async(self) + restore_os_env(old_os_env) + + @pytest.mark.asyncio + async def test_set_status_injected_for_app_mention(self): + app = AsyncApp(client=self.web_client) + + @app.event("app_mention") + async def handle_mention(set_status: AsyncSetStatus, context: AsyncBoltContext): + assert set_status is not None + assert isinstance(set_status, AsyncSetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "C111" + assert set_status.thread_ts == "1595926230.009600" + await set_status(status="Thinking...") + + request = AsyncBoltRequest(body=app_mention_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + await assert_received_request_count_async(self, path="/assistant.threads.setStatus", min_count=1) + + @pytest.mark.asyncio + async def test_set_status_injected_for_threaded_message(self): + app = AsyncApp(client=self.web_client) + + @app.event("message") + async def handle_message(set_status: AsyncSetStatus, context: AsyncBoltContext): + assert set_status is not None + assert isinstance(set_status, AsyncSetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "D111" + assert set_status.thread_ts == "1726133698.626339" + await set_status(status="Thinking...") + + request = AsyncBoltRequest(body=threaded_user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + await assert_received_request_count_async(self, path="/assistant.threads.setStatus", min_count=1) + + @pytest.mark.asyncio + async def test_set_status_in_user_message(self): + app = AsyncApp(client=self.web_client) + + @app.message("") + async def handle_user_message(set_status: AsyncSetStatus, context: AsyncBoltContext): + assert set_status is not None + assert isinstance(set_status, AsyncSetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "C111" + assert set_status.thread_ts == "1610261659.001400" + await set_status(status="Thinking...") + + request = AsyncBoltRequest(body=user_message_event_payload, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + await assert_received_request_count_async(self, path="/assistant.threads.setStatus", min_count=1) + + @pytest.mark.asyncio + async def test_set_status_in_bot_message(self): + app = AsyncApp(client=self.web_client) + + @app.message("") + async def handle_user_message(set_status: AsyncSetStatus, context: AsyncBoltContext): + assert set_status is not None + assert isinstance(set_status, AsyncSetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "C111" + assert set_status.thread_ts == "1610261539.000900" + await set_status(status="Thinking...") + + request = AsyncBoltRequest(body=bot_message_event_payload, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + await assert_received_request_count_async(self, path="/assistant.threads.setStatus", min_count=1) + + @pytest.mark.asyncio + async def test_set_status_in_assistant_thread_started(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + + @assistant.thread_started + async def start_thread(set_status: AsyncSetStatus, context: AsyncBoltContext): + assert set_status is not None + assert isinstance(set_status, AsyncSetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "D111" + assert set_status.thread_ts == "1726133698.626339" + await set_status(status="Thinking...") + + app.assistant(assistant) + + request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + await assert_received_request_count_async(self, path="/assistant.threads.setStatus", min_count=1) + + @pytest.mark.asyncio + async def test_set_status_in_assistant_user_message(self): + app = AsyncApp(client=self.web_client) + assistant = AsyncAssistant() + + @assistant.user_message + async def handle_user_message(set_status: AsyncSetStatus, context: AsyncBoltContext): + assert set_status is not None + assert isinstance(set_status, AsyncSetStatus) + assert set_status == context.set_status + assert set_status.channel_id == "D111" + assert set_status.thread_ts == "1726133698.626339" + await set_status(status="Thinking...") + + app.assistant(assistant) + + request = AsyncBoltRequest(body=threaded_user_message_event_body, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + await assert_received_request_count_async(self, path="/assistant.threads.setStatus", min_count=1) + + @pytest.mark.asyncio + async def test_set_status_is_none_for_view_submission(self): + app = AsyncApp(client=self.web_client, request_verification_enabled=False) + listener_called = asyncio.Event() + + @app.view("view-id") + async def handle_view(ack, set_status, context: AsyncBoltContext): + await ack() + assert set_status is None + assert context.set_status is None + listener_called.set() + + request = AsyncBoltRequest( + body=f"payload={quote(json.dumps(view_submission_body))}", + ) + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_auth_test_count_async(self, 1) + assert listener_called.is_set() diff --git a/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py b/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py index f56bd2e62..8e626fd0c 100644 --- a/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py +++ b/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py @@ -14,7 +14,7 @@ def next(): return BoltResponse(status=200) -AGENT_KWARGS = ("say", "set_status", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") +ASSISTANT_KWARGS = ("say", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") class TestAttachingAgentKwargs: @@ -26,9 +26,11 @@ def test_assistant_event_attaches_kwargs(self): resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key in req.context, f"{key} should be set on context" assert req.context["say"].thread_ts == "1726133698.626339" + assert "say_stream" in req.context + assert "set_status" in req.context def test_user_message_event_attaches_kwargs(self): middleware = AttachingAgentKwargs() @@ -38,9 +40,11 @@ def test_user_message_event_attaches_kwargs(self): resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key in req.context, f"{key} should be set on context" assert req.context["say"].thread_ts == "1726133698.626339" + assert "say_stream" in req.context + assert "set_status" in req.context def test_non_assistant_event_does_not_attach_kwargs(self): middleware = AttachingAgentKwargs() @@ -50,8 +54,10 @@ def test_non_assistant_event_does_not_attach_kwargs(self): resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key not in req.context, f"{key} should not be set on context" + assert "say_stream" in req.context + assert "set_status" in req.context def test_non_event_does_not_attach_kwargs(self): middleware = AttachingAgentKwargs() @@ -60,5 +66,7 @@ def test_non_event_does_not_attach_kwargs(self): resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key not in req.context, f"{key} should not be set on context" + assert "say_stream" not in req.context + assert "set_status" not in req.context diff --git a/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py b/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py index 55883e5f3..61aa0b59e 100644 --- a/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py +++ b/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py @@ -15,7 +15,7 @@ async def next(): return BoltResponse(status=200) -AGENT_KWARGS = ("say", "set_status", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") +ASSISTANT_KWARGS = ("say", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") class TestAsyncAttachingAgentKwargs: @@ -28,9 +28,11 @@ async def test_assistant_event_attaches_kwargs(self): resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key in req.context, f"{key} should be set on context" assert req.context["say"].thread_ts == "1726133698.626339" + assert "say_stream" in req.context + assert "set_status" in req.context @pytest.mark.asyncio async def test_user_message_event_attaches_kwargs(self): @@ -41,9 +43,11 @@ async def test_user_message_event_attaches_kwargs(self): resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key in req.context, f"{key} should be set on context" assert req.context["say"].thread_ts == "1726133698.626339" + assert "say_stream" in req.context + assert "set_status" in req.context @pytest.mark.asyncio async def test_non_assistant_event_does_not_attach_kwargs(self): @@ -54,8 +58,10 @@ async def test_non_assistant_event_does_not_attach_kwargs(self): resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key not in req.context, f"{key} should not be set on context" + assert "say_stream" in req.context + assert "set_status" in req.context @pytest.mark.asyncio async def test_non_event_does_not_attach_kwargs(self): @@ -65,5 +71,7 @@ async def test_non_event_does_not_attach_kwargs(self): resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) assert resp.status == 200 - for key in AGENT_KWARGS: + for key in ASSISTANT_KWARGS: assert key not in req.context, f"{key} should not be set on context" + assert "say_stream" not in req.context + assert "set_status" not in req.context From 6406058f35f3eee85539e73f87e995ee74ece2ed Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 20 Mar 2026 07:11:21 -0700 Subject: [PATCH 202/282] fix: Remove 'agent: BoltAgent' listener argument (#1466) --- AGENTS.md | 4 +- slack_bolt/__init__.py | 2 - slack_bolt/agent/__init__.py | 5 - slack_bolt/agent/agent.py | 139 ------ slack_bolt/agent/async_agent.py | 138 ------ slack_bolt/kwargs_injection/args.py | 5 - slack_bolt/kwargs_injection/async_args.py | 5 - slack_bolt/kwargs_injection/async_utils.py | 22 - slack_bolt/kwargs_injection/utils.py | 22 - tests/scenario_tests/test_events_agent.py | 162 ------- .../scenario_tests_async/test_events_agent.py | 169 -------- tests/slack_bolt/agent/__init__.py | 0 tests/slack_bolt/agent/test_agent.py | 365 ---------------- tests/slack_bolt_async/agent/__init__.py | 0 .../agent/test_async_agent.py | 399 ------------------ 15 files changed, 2 insertions(+), 1435 deletions(-) delete mode 100644 slack_bolt/agent/__init__.py delete mode 100644 slack_bolt/agent/agent.py delete mode 100644 slack_bolt/agent/async_agent.py delete mode 100644 tests/scenario_tests/test_events_agent.py delete mode 100644 tests/scenario_tests_async/test_events_agent.py delete mode 100644 tests/slack_bolt/agent/__init__.py delete mode 100644 tests/slack_bolt/agent/test_agent.py delete mode 100644 tests/slack_bolt_async/agent/__init__.py delete mode 100644 tests/slack_bolt_async/agent/test_async_agent.py diff --git a/AGENTS.md b/AGENTS.md index 57f2fa588..892a858e7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -152,7 +152,7 @@ For FaaS environments (`process_before_response=True`), long-running handlers ex ### Kwargs Injection -Listeners receive arguments by parameter name. The framework inspects function signatures and injects matching args: `body`, `event`, `action`, `command`, `payload`, `context`, `client`, `ack`, `say`, `respond`, `logger`, `complete`, `fail`, `agent`, etc. Defined in `slack_bolt/kwargs_injection/args.py`. +Listeners receive arguments by parameter name. The framework inspects function signatures and injects matching args: `body`, `event`, `action`, `command`, `payload`, `context`, `client`, `ack`, `say`, `respond`, `logger`, `complete`, `fail`, etc. Defined in `slack_bolt/kwargs_injection/args.py`. ### Adapter System @@ -160,7 +160,7 @@ Each adapter in `slack_bolt/adapter/` converts between a web framework's request ### AI Agents & Assistants -`BoltAgent` (`slack_bolt/agent/`) provides `chat_stream()`, `set_status()`, and `set_suggested_prompts()` for AI-powered agents. `Assistant` middleware (`slack_bolt/middleware/assistant/`) handles assistant thread events. +`Assistant` middleware (`slack_bolt/middleware/assistant/`) handles assistant thread events. ## Key Development Patterns diff --git a/slack_bolt/__init__.py b/slack_bolt/__init__.py index dfe950bf2..d85453950 100644 --- a/slack_bolt/__init__.py +++ b/slack_bolt/__init__.py @@ -22,7 +22,6 @@ from .response import BoltResponse # AI Agents & Assistants -from .agent import BoltAgent from .middleware.assistant.assistant import ( Assistant, ) @@ -49,7 +48,6 @@ "CustomListenerMatcher", "BoltRequest", "BoltResponse", - "BoltAgent", "Assistant", "AssistantThreadContext", "AssistantThreadContextStore", diff --git a/slack_bolt/agent/__init__.py b/slack_bolt/agent/__init__.py deleted file mode 100644 index 4d751f27f..000000000 --- a/slack_bolt/agent/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .agent import BoltAgent - -__all__ = [ - "BoltAgent", -] diff --git a/slack_bolt/agent/agent.py b/slack_bolt/agent/agent.py deleted file mode 100644 index 523b0e33c..000000000 --- a/slack_bolt/agent/agent.py +++ /dev/null @@ -1,139 +0,0 @@ -from typing import Dict, List, Optional, Sequence, Union - -from slack_sdk import WebClient -from slack_sdk.web import SlackResponse -from slack_sdk.web.chat_stream import ChatStream - - -class BoltAgent: - """Agent listener argument for building AI-powered Slack agents. - - Experimental: - This API is experimental and may change in future releases. - - @app.event("app_mention") - def handle_mention(agent): - stream = agent.chat_stream() - stream.append(markdown_text="Hello!") - stream.stop() - """ - - def __init__( - self, - *, - client: WebClient, - channel_id: Optional[str] = None, - thread_ts: Optional[str] = None, - ts: Optional[str] = None, - team_id: Optional[str] = None, - user_id: Optional[str] = None, - ): - self._client = client - self._channel_id = channel_id - self._thread_ts = thread_ts - self._ts = ts - self._team_id = team_id - self._user_id = user_id - - def chat_stream( - self, - *, - channel: Optional[str] = None, - thread_ts: Optional[str] = None, - recipient_team_id: Optional[str] = None, - recipient_user_id: Optional[str] = None, - **kwargs, - ) -> ChatStream: - """Creates a ChatStream with defaults from event context. - - Each call creates a new instance. Create multiple for parallel streams. - - Args: - channel: Channel ID. Defaults to the channel from the event context. - thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. - recipient_team_id: Team ID of the recipient. Defaults to the team from the event context. - recipient_user_id: User ID of the recipient. Defaults to the user from the event context. - **kwargs: Additional arguments passed to ``WebClient.chat_stream()``. - - Returns: - A new ``ChatStream`` instance. - """ - provided = [arg for arg in (channel, thread_ts, recipient_team_id, recipient_user_id) if arg is not None] - if provided and len(provided) < 4: - raise ValueError( - "Either provide all of channel, thread_ts, recipient_team_id, and recipient_user_id, or none of them" - ) - # Argument validation is delegated to chat_stream() and the API - return self._client.chat_stream( - channel=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] - recipient_team_id=recipient_team_id or self._team_id, - recipient_user_id=recipient_user_id or self._user_id, - **kwargs, - ) - - def set_status( - self, - *, - status: str, - loading_messages: Optional[List[str]] = None, - channel_id: Optional[str] = None, - thread_ts: Optional[str] = None, - **kwargs, - ) -> SlackResponse: - """Sets the status of an assistant thread. - - Args: - status: The status text to display. - loading_messages: Optional list of loading messages to cycle through. - channel_id: Channel ID. Defaults to the channel from the event context. - thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. - **kwargs: Additional arguments passed to ``WebClient.assistant_threads_setStatus()``. - - Returns: - ``SlackResponse`` from the API call. - """ - return self._client.assistant_threads_setStatus( - channel_id=channel_id or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] - status=status, - loading_messages=loading_messages, - **kwargs, - ) - - def set_suggested_prompts( - self, - *, - prompts: Sequence[Union[str, Dict[str, str]]], - title: Optional[str] = None, - channel_id: Optional[str] = None, - thread_ts: Optional[str] = None, - **kwargs, - ) -> SlackResponse: - """Sets suggested prompts for an assistant thread. - - Args: - prompts: A sequence of prompts. Each prompt can be either a string - (used as both title and message) or a dict with 'title' and 'message' keys. - title: Optional title for the suggested prompts section. - channel_id: Channel ID. Defaults to the channel from the event context. - thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. - **kwargs: Additional arguments passed to ``WebClient.assistant_threads_setSuggestedPrompts()``. - - Returns: - ``SlackResponse`` from the API call. - """ - prompts_arg: List[Dict[str, str]] = [] - for prompt in prompts: - if isinstance(prompt, str): - prompts_arg.append({"title": prompt, "message": prompt}) - else: - prompts_arg.append(prompt) - - return self._client.assistant_threads_setSuggestedPrompts( - channel_id=channel_id or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] - prompts=prompts_arg, - title=title, - **kwargs, - ) diff --git a/slack_bolt/agent/async_agent.py b/slack_bolt/agent/async_agent.py deleted file mode 100644 index da4ec6c0a..000000000 --- a/slack_bolt/agent/async_agent.py +++ /dev/null @@ -1,138 +0,0 @@ -from typing import Dict, List, Optional, Sequence, Union - -from slack_sdk.web.async_client import AsyncSlackResponse, AsyncWebClient -from slack_sdk.web.async_chat_stream import AsyncChatStream - - -class AsyncBoltAgent: - """Async agent listener argument for building AI-powered Slack agents. - - Experimental: - This API is experimental and may change in future releases. - - @app.event("app_mention") - async def handle_mention(agent): - stream = await agent.chat_stream() - await stream.append(markdown_text="Hello!") - await stream.stop() - """ - - def __init__( - self, - *, - client: AsyncWebClient, - channel_id: Optional[str] = None, - thread_ts: Optional[str] = None, - ts: Optional[str] = None, - team_id: Optional[str] = None, - user_id: Optional[str] = None, - ): - self._client = client - self._channel_id = channel_id - self._thread_ts = thread_ts - self._ts = ts - self._team_id = team_id - self._user_id = user_id - - async def chat_stream( - self, - *, - channel: Optional[str] = None, - thread_ts: Optional[str] = None, - recipient_team_id: Optional[str] = None, - recipient_user_id: Optional[str] = None, - **kwargs, - ) -> AsyncChatStream: - """Creates an AsyncChatStream with defaults from event context. - - Each call creates a new instance. Create multiple for parallel streams. - - Args: - channel: Channel ID. Defaults to the channel from the event context. - thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. - recipient_team_id: Team ID of the recipient. Defaults to the team from the event context. - recipient_user_id: User ID of the recipient. Defaults to the user from the event context. - **kwargs: Additional arguments passed to ``AsyncWebClient.chat_stream()``. - - Returns: - A new ``AsyncChatStream`` instance. - """ - provided = [arg for arg in (channel, thread_ts, recipient_team_id, recipient_user_id) if arg is not None] - if provided and len(provided) < 4: - raise ValueError( - "Either provide all of channel, thread_ts, recipient_team_id, and recipient_user_id, or none of them" - ) - # Argument validation is delegated to chat_stream() and the API - return await self._client.chat_stream( - channel=channel or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] - recipient_team_id=recipient_team_id or self._team_id, - recipient_user_id=recipient_user_id or self._user_id, - **kwargs, - ) - - async def set_status( - self, - *, - status: str, - loading_messages: Optional[List[str]] = None, - channel_id: Optional[str] = None, - thread_ts: Optional[str] = None, - **kwargs, - ) -> AsyncSlackResponse: - """Sets the status of an assistant thread. - - Args: - status: The status text to display. - loading_messages: Optional list of loading messages to cycle through. - channel_id: Channel ID. Defaults to the channel from the event context. - thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. - **kwargs: Additional arguments passed to ``AsyncWebClient.assistant_threads_setStatus()``. - - Returns: - ``AsyncSlackResponse`` from the API call. - """ - return await self._client.assistant_threads_setStatus( - channel_id=channel_id or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] - status=status, - loading_messages=loading_messages, - **kwargs, - ) - - async def set_suggested_prompts( - self, - *, - prompts: Sequence[Union[str, Dict[str, str]]], - title: Optional[str] = None, - channel_id: Optional[str] = None, - thread_ts: Optional[str] = None, - **kwargs, - ) -> AsyncSlackResponse: - """Sets suggested prompts for an assistant thread. - - Args: - prompts: A sequence of prompts. Each prompt can be either a string - (used as both title and message) or a dict with 'title' and 'message' keys. - title: Optional title for the suggested prompts section. - channel_id: Channel ID. Defaults to the channel from the event context. - thread_ts: Thread timestamp. Defaults to the thread_ts from the event context. - **kwargs: Additional arguments passed to ``AsyncWebClient.assistant_threads_setSuggestedPrompts()``. - - Returns: - ``AsyncSlackResponse`` from the API call. - """ - prompts_arg: List[Dict[str, str]] = [] - for prompt in prompts: - if isinstance(prompt, str): - prompts_arg.append({"title": prompt, "message": prompt}) - else: - prompts_arg.append(prompt) - - return await self._client.assistant_threads_setSuggestedPrompts( - channel_id=channel_id or self._channel_id, # type: ignore[arg-type] - thread_ts=thread_ts or self._thread_ts or self._ts, # type: ignore[arg-type] - prompts=prompts_arg, - title=title, - **kwargs, - ) diff --git a/slack_bolt/kwargs_injection/args.py b/slack_bolt/kwargs_injection/args.py index dfb242fd1..4cd70176d 100644 --- a/slack_bolt/kwargs_injection/args.py +++ b/slack_bolt/kwargs_injection/args.py @@ -8,7 +8,6 @@ from slack_bolt.context.fail import Fail from slack_bolt.context.get_thread_context.get_thread_context import GetThreadContext from slack_bolt.context.respond import Respond -from slack_bolt.agent.agent import BoltAgent from slack_bolt.context.save_thread_context import SaveThreadContext from slack_bolt.context.say import Say from slack_bolt.context.say_stream import SayStream @@ -104,8 +103,6 @@ def handle_buttons(args): """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" - agent: Optional[BoltAgent] - """`agent` listener argument for AI Agents & Assistants""" say_stream: Optional[SayStream] """`say_stream()` utility function for AI Agents & Assistants""" # middleware @@ -141,7 +138,6 @@ def __init__( set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, - agent: Optional[BoltAgent] = None, say_stream: Optional[SayStream] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects @@ -176,7 +172,6 @@ def __init__( self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context - self.agent = agent self.say_stream = say_stream self.next: Callable[[], None] = next diff --git a/slack_bolt/kwargs_injection/async_args.py b/slack_bolt/kwargs_injection/async_args.py index 19719e900..2217cfe9f 100644 --- a/slack_bolt/kwargs_injection/async_args.py +++ b/slack_bolt/kwargs_injection/async_args.py @@ -1,7 +1,6 @@ from logging import Logger from typing import Callable, Awaitable, Dict, Any, Optional -from slack_bolt.agent.async_agent import AsyncBoltAgent from slack_bolt.context.ack.async_ack import AsyncAck from slack_bolt.context.async_context import AsyncBoltContext from slack_bolt.context.complete.async_complete import AsyncComplete @@ -103,8 +102,6 @@ async def handle_buttons(args): """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[AsyncSaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" - agent: Optional[AsyncBoltAgent] - """`agent` listener argument for AI Agents & Assistants""" say_stream: Optional[AsyncSayStream] """`say_stream()` utility function for AI Agents & Assistants""" # middleware @@ -140,7 +137,6 @@ def __init__( set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] = None, get_thread_context: Optional[AsyncGetThreadContext] = None, save_thread_context: Optional[AsyncSaveThreadContext] = None, - agent: Optional[AsyncBoltAgent] = None, say_stream: Optional[AsyncSayStream] = None, next: Callable[[], Awaitable[None]], **kwargs, # noqa @@ -172,7 +168,6 @@ def __init__( self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context - self.agent = agent self.say_stream = say_stream self.next: Callable[[], Awaitable[None]] = next diff --git a/slack_bolt/kwargs_injection/async_utils.py b/slack_bolt/kwargs_injection/async_utils.py index 534fb6133..246fd10c9 100644 --- a/slack_bolt/kwargs_injection/async_utils.py +++ b/slack_bolt/kwargs_injection/async_utils.py @@ -1,11 +1,9 @@ import inspect import logging -import warnings from typing import Callable, Dict, MutableSequence, Optional, Any from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse -from slack_bolt.warning import ExperimentalWarning from .async_args import AsyncArgs from slack_bolt.request.payload_utils import ( to_options, @@ -86,26 +84,6 @@ def build_async_required_kwargs( if k not in all_available_args: all_available_args[k] = v - # Defer agent creation to avoid constructing AsyncBoltAgent on every request - if "agent" in required_arg_names: - from slack_bolt.agent.async_agent import AsyncBoltAgent - - event = request.body.get("event", {}) - - all_available_args["agent"] = AsyncBoltAgent( - client=request.context.client, - channel_id=request.context.channel_id, - thread_ts=request.context.thread_ts or event.get("thread_ts"), - ts=event.get("ts"), - team_id=request.context.team_id, - user_id=request.context.user_id, - ) - warnings.warn( - "The agent listener argument is experimental and may change in future versions.", - category=ExperimentalWarning, - stacklevel=2, # Point to the caller, not this internal helper - ) - if len(required_arg_names) > 0: # To support instance/class methods in a class for listeners/middleware, # check if the first argument is either self or cls diff --git a/slack_bolt/kwargs_injection/utils.py b/slack_bolt/kwargs_injection/utils.py index 101e00099..218fbeb6e 100644 --- a/slack_bolt/kwargs_injection/utils.py +++ b/slack_bolt/kwargs_injection/utils.py @@ -1,11 +1,9 @@ import inspect import logging -import warnings from typing import Callable, Dict, MutableSequence, Optional, Any from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse -from slack_bolt.warning import ExperimentalWarning from .args import Args from slack_bolt.request.payload_utils import ( to_options, @@ -85,26 +83,6 @@ def build_required_kwargs( if k not in all_available_args: all_available_args[k] = v - # Defer agent creation to avoid constructing BoltAgent on every request - if "agent" in required_arg_names: - from slack_bolt.agent.agent import BoltAgent - - event = request.body.get("event", {}) - - all_available_args["agent"] = BoltAgent( - client=request.context.client, - channel_id=request.context.channel_id, - thread_ts=request.context.thread_ts or event.get("thread_ts"), - ts=event.get("ts"), - team_id=request.context.team_id, - user_id=request.context.user_id, - ) - warnings.warn( - "The agent listener argument is experimental and may change in future versions.", - category=ExperimentalWarning, - stacklevel=2, # Point to the caller, not this internal helper - ) - if len(required_arg_names) > 0: # To support instance/class methods in a class for listeners/middleware, # check if the first argument is either self or cls diff --git a/tests/scenario_tests/test_events_agent.py b/tests/scenario_tests/test_events_agent.py deleted file mode 100644 index 667739728..000000000 --- a/tests/scenario_tests/test_events_agent.py +++ /dev/null @@ -1,162 +0,0 @@ -import json -from time import sleep - -import pytest -from slack_sdk.web import WebClient - -from slack_bolt import App, BoltRequest, BoltContext, BoltAgent -from slack_bolt.agent.agent import BoltAgent as BoltAgentDirect -from slack_bolt.warning import ExperimentalWarning -from tests.mock_web_api_server import ( - setup_mock_web_api_server, - cleanup_mock_web_api_server, -) -from tests.utils import remove_os_env_temporarily, restore_os_env - - -class TestEventsAgent: - valid_token = "xoxb-valid" - mock_api_server_base_url = "http://localhost:8888" - web_client = WebClient( - token=valid_token, - base_url=mock_api_server_base_url, - ) - - def setup_method(self): - self.old_os_env = remove_os_env_temporarily() - setup_mock_web_api_server(self) - - def teardown_method(self): - cleanup_mock_web_api_server(self) - restore_os_env(self.old_os_env) - - def test_agent_injected_for_app_mention(self): - app = App(client=self.web_client) - - state = {"called": False} - - def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False - - @app.event("app_mention") - def handle_mention(agent: BoltAgent, context: BoltContext): - assert agent is not None - assert isinstance(agent, BoltAgentDirect) - assert context.channel_id == "C111" - state["called"] = True - - request = BoltRequest(body=app_mention_event_body, mode="socket_mode") - response = app.dispatch(request) - assert response.status == 200 - assert_target_called() - - def test_agent_available_in_action_listener(self): - app = App(client=self.web_client) - - state = {"called": False} - - def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False - - @app.action("test_action") - def handle_action(ack, agent: BoltAgent): - ack() - assert agent is not None - assert isinstance(agent, BoltAgentDirect) - state["called"] = True - - request = BoltRequest(body=json.dumps(action_event_body), mode="socket_mode") - response = app.dispatch(request) - assert response.status == 200 - assert_target_called() - - def test_agent_kwarg_emits_experimental_warning(self): - app = App(client=self.web_client) - - state = {"called": False} - - def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False - - @app.event("app_mention") - def handle_mention(agent: BoltAgent): - state["called"] = True - - request = BoltRequest(body=app_mention_event_body, mode="socket_mode") - with pytest.warns(ExperimentalWarning, match="agent listener argument is experimental"): - response = app.dispatch(request) - assert response.status == 200 - assert_target_called() - - -# ---- Test event bodies ---- - - -def build_payload(event: dict) -> dict: - return { - "token": "verification_token", - "team_id": "T111", - "enterprise_id": "E111", - "api_app_id": "A111", - "event": event, - "type": "event_callback", - "event_id": "Ev111", - "event_time": 1599616881, - "authorizations": [ - { - "enterprise_id": "E111", - "team_id": "T111", - "user_id": "W111", - "is_bot": True, - "is_enterprise_install": False, - } - ], - } - - -app_mention_event_body = build_payload( - { - "type": "app_mention", - "user": "W222", - "text": "<@W111> hello", - "ts": "1234567890.123456", - "channel": "C111", - "event_ts": "1234567890.123456", - } -) - -action_event_body = { - "type": "block_actions", - "user": {"id": "W222", "username": "test_user", "name": "test_user", "team_id": "T111"}, - "api_app_id": "A111", - "token": "verification_token", - "container": {"type": "message", "message_ts": "1234567890.123456", "channel_id": "C111", "is_ephemeral": False}, - "channel": {"id": "C111", "name": "test-channel"}, - "team": {"id": "T111", "domain": "test"}, - "enterprise": {"id": "E111", "name": "test"}, - "trigger_id": "111.222.xxx", - "actions": [ - { - "type": "button", - "block_id": "b", - "action_id": "test_action", - "text": {"type": "plain_text", "text": "Button"}, - "action_ts": "1234567890.123456", - } - ], -} diff --git a/tests/scenario_tests_async/test_events_agent.py b/tests/scenario_tests_async/test_events_agent.py deleted file mode 100644 index 1702cdb61..000000000 --- a/tests/scenario_tests_async/test_events_agent.py +++ /dev/null @@ -1,169 +0,0 @@ -import asyncio -import json - -import pytest -from slack_sdk.web.async_client import AsyncWebClient - -from slack_bolt.agent.async_agent import AsyncBoltAgent -from slack_bolt.app.async_app import AsyncApp -from slack_bolt.context.async_context import AsyncBoltContext -from slack_bolt.request.async_request import AsyncBoltRequest -from slack_bolt.warning import ExperimentalWarning -from tests.mock_web_api_server import ( - cleanup_mock_web_api_server_async, - setup_mock_web_api_server_async, -) -from tests.utils import remove_os_env_temporarily, restore_os_env - - -class TestAsyncEventsAgent: - valid_token = "xoxb-valid" - mock_api_server_base_url = "http://localhost:8888" - web_client = AsyncWebClient( - token=valid_token, - base_url=mock_api_server_base_url, - ) - - @pytest.fixture(scope="function", autouse=True) - def setup_teardown(self): - old_os_env = remove_os_env_temporarily() - setup_mock_web_api_server_async(self) - try: - yield - finally: - cleanup_mock_web_api_server_async(self) - restore_os_env(old_os_env) - - @pytest.mark.asyncio - async def test_agent_injected_for_app_mention(self): - app = AsyncApp(client=self.web_client) - - state = {"called": False} - - async def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - await asyncio.sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False - - @app.event("app_mention") - async def handle_mention(agent: AsyncBoltAgent, context: AsyncBoltContext): - assert agent is not None - assert isinstance(agent, AsyncBoltAgent) - assert context.channel_id == "C111" - state["called"] = True - - request = AsyncBoltRequest(body=app_mention_event_body, mode="socket_mode") - response = await app.async_dispatch(request) - assert response.status == 200 - await assert_target_called() - - @pytest.mark.asyncio - async def test_agent_available_in_action_listener(self): - app = AsyncApp(client=self.web_client) - - state = {"called": False} - - async def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - await asyncio.sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False - - @app.action("test_action") - async def handle_action(ack, agent: AsyncBoltAgent): - await ack() - assert agent is not None - assert isinstance(agent, AsyncBoltAgent) - state["called"] = True - - request = AsyncBoltRequest(body=json.dumps(action_event_body), mode="socket_mode") - response = await app.async_dispatch(request) - assert response.status == 200 - await assert_target_called() - - @pytest.mark.asyncio - async def test_agent_kwarg_emits_experimental_warning(self): - app = AsyncApp(client=self.web_client) - - state = {"called": False} - - async def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - await asyncio.sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False - - @app.event("app_mention") - async def handle_mention(agent: AsyncBoltAgent): - state["called"] = True - - request = AsyncBoltRequest(body=app_mention_event_body, mode="socket_mode") - with pytest.warns(ExperimentalWarning, match="agent listener argument is experimental"): - response = await app.async_dispatch(request) - assert response.status == 200 - await assert_target_called() - - -# ---- Test event bodies ---- - - -def build_payload(event: dict) -> dict: - return { - "token": "verification_token", - "team_id": "T111", - "enterprise_id": "E111", - "api_app_id": "A111", - "event": event, - "type": "event_callback", - "event_id": "Ev111", - "event_time": 1599616881, - "authorizations": [ - { - "enterprise_id": "E111", - "team_id": "T111", - "user_id": "W111", - "is_bot": True, - "is_enterprise_install": False, - } - ], - } - - -app_mention_event_body = build_payload( - { - "type": "app_mention", - "user": "W222", - "text": "<@W111> hello", - "ts": "1234567890.123456", - "channel": "C111", - "event_ts": "1234567890.123456", - } -) - -action_event_body = { - "type": "block_actions", - "user": {"id": "W222", "username": "test_user", "name": "test_user", "team_id": "T111"}, - "api_app_id": "A111", - "token": "verification_token", - "container": {"type": "message", "message_ts": "1234567890.123456", "channel_id": "C111", "is_ephemeral": False}, - "channel": {"id": "C111", "name": "test-channel"}, - "team": {"id": "T111", "domain": "test"}, - "enterprise": {"id": "E111", "name": "test"}, - "trigger_id": "111.222.xxx", - "actions": [ - { - "type": "button", - "block_id": "b", - "action_id": "test_action", - "text": {"type": "plain_text", "text": "Button"}, - "action_ts": "1234567890.123456", - } - ], -} diff --git a/tests/slack_bolt/agent/__init__.py b/tests/slack_bolt/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/slack_bolt/agent/test_agent.py b/tests/slack_bolt/agent/test_agent.py deleted file mode 100644 index 76ac7d17b..000000000 --- a/tests/slack_bolt/agent/test_agent.py +++ /dev/null @@ -1,365 +0,0 @@ -from unittest.mock import MagicMock - -import pytest -from slack_sdk.web import WebClient -from slack_sdk.web.chat_stream import ChatStream - -from slack_bolt.agent.agent import BoltAgent - - -class TestBoltAgent: - def test_chat_stream_uses_context_defaults(self): - """BoltAgent.chat_stream() passes context defaults to WebClient.chat_stream().""" - client = MagicMock(spec=WebClient) - client.chat_stream.return_value = MagicMock(spec=ChatStream) - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - stream = agent.chat_stream() - - client.chat_stream.assert_called_once_with( - channel="C111", - thread_ts="1234567890.123456", - recipient_team_id="T111", - recipient_user_id="W222", - ) - assert stream is not None - - def test_chat_stream_overrides_context_defaults(self): - """Explicit kwargs to chat_stream() override context defaults.""" - client = MagicMock(spec=WebClient) - client.chat_stream.return_value = MagicMock(spec=ChatStream) - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - stream = agent.chat_stream( - channel="C999", - thread_ts="9999999999.999999", - recipient_team_id="T999", - recipient_user_id="U999", - ) - - client.chat_stream.assert_called_once_with( - channel="C999", - thread_ts="9999999999.999999", - recipient_team_id="T999", - recipient_user_id="U999", - ) - assert stream is not None - - def test_chat_stream_rejects_partial_overrides(self): - """Passing only some of the four context args raises ValueError.""" - client = MagicMock(spec=WebClient) - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - with pytest.raises(ValueError, match="Either provide all of"): - agent.chat_stream(channel="C999") - - def test_chat_stream_passes_extra_kwargs(self): - """Extra kwargs are forwarded to WebClient.chat_stream().""" - client = MagicMock(spec=WebClient) - client.chat_stream.return_value = MagicMock(spec=ChatStream) - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.chat_stream(buffer_size=512) - - client.chat_stream.assert_called_once_with( - channel="C111", - thread_ts="1234567890.123456", - recipient_team_id="T111", - recipient_user_id="W222", - buffer_size=512, - ) - - def test_chat_stream_falls_back_to_ts(self): - """When thread_ts is not set, chat_stream() falls back to ts.""" - client = MagicMock(spec=WebClient) - client.chat_stream.return_value = MagicMock(spec=ChatStream) - - agent = BoltAgent( - client=client, - channel_id="C111", - team_id="T111", - ts="1111111111.111111", - user_id="W222", - ) - stream = agent.chat_stream() - - client.chat_stream.assert_called_once_with( - channel="C111", - thread_ts="1111111111.111111", - recipient_team_id="T111", - recipient_user_id="W222", - ) - assert stream is not None - - def test_chat_stream_prefers_thread_ts_over_ts(self): - """thread_ts takes priority over ts.""" - client = MagicMock(spec=WebClient) - client.chat_stream.return_value = MagicMock(spec=ChatStream) - - agent = BoltAgent( - client=client, - channel_id="C111", - team_id="T111", - thread_ts="1234567890.123456", - ts="1111111111.111111", - user_id="W222", - ) - stream = agent.chat_stream() - - client.chat_stream.assert_called_once_with( - channel="C111", - thread_ts="1234567890.123456", - recipient_team_id="T111", - recipient_user_id="W222", - ) - assert stream is not None - - def test_set_status_uses_context_defaults(self): - """BoltAgent.set_status() passes context defaults to WebClient.assistant_threads_setStatus().""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setStatus.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_status(status="Thinking...") - - client.assistant_threads_setStatus.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - status="Thinking...", - loading_messages=None, - ) - - def test_set_status_with_loading_messages(self): - """BoltAgent.set_status() forwards loading_messages.""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setStatus.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_status( - status="Thinking...", - loading_messages=["Sitting...", "Waiting..."], - ) - - client.assistant_threads_setStatus.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - status="Thinking...", - loading_messages=["Sitting...", "Waiting..."], - ) - - def test_set_status_overrides_context_defaults(self): - """Explicit channel_id/thread_ts override context defaults.""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setStatus.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_status( - status="Thinking...", - channel_id="C999", - thread_ts="9999999999.999999", - ) - - client.assistant_threads_setStatus.assert_called_once_with( - channel_id="C999", - thread_ts="9999999999.999999", - status="Thinking...", - loading_messages=None, - ) - - def test_set_status_passes_extra_kwargs(self): - """Extra kwargs are forwarded to WebClient.assistant_threads_setStatus().""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setStatus.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_status(status="Thinking...", token="xoxb-override") - - client.assistant_threads_setStatus.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - status="Thinking...", - loading_messages=None, - token="xoxb-override", - ) - - def test_set_status_requires_status(self): - """set_status() raises TypeError when status is not provided.""" - client = MagicMock(spec=WebClient) - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - with pytest.raises(TypeError): - agent.set_status() - - def test_set_suggested_prompts_uses_context_defaults(self): - """BoltAgent.set_suggested_prompts() passes context defaults to WebClient.assistant_threads_setSuggestedPrompts().""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_suggested_prompts(prompts=["What can you do?", "Help me write code"]) - - client.assistant_threads_setSuggestedPrompts.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - prompts=[ - {"title": "What can you do?", "message": "What can you do?"}, - {"title": "Help me write code", "message": "Help me write code"}, - ], - title=None, - ) - - def test_set_suggested_prompts_with_dict_prompts(self): - """BoltAgent.set_suggested_prompts() accepts dict prompts with title and message.""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_suggested_prompts( - prompts=[ - {"title": "Short title", "message": "A much longer message for this prompt"}, - ], - title="Suggestions", - ) - - client.assistant_threads_setSuggestedPrompts.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - prompts=[ - {"title": "Short title", "message": "A much longer message for this prompt"}, - ], - title="Suggestions", - ) - - def test_set_suggested_prompts_overrides_context_defaults(self): - """Explicit channel_id/thread_ts override context defaults.""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_suggested_prompts( - prompts=["Hello"], - channel_id="C999", - thread_ts="9999999999.999999", - ) - - client.assistant_threads_setSuggestedPrompts.assert_called_once_with( - channel_id="C999", - thread_ts="9999999999.999999", - prompts=[{"title": "Hello", "message": "Hello"}], - title=None, - ) - - def test_set_suggested_prompts_passes_extra_kwargs(self): - """Extra kwargs are forwarded to WebClient.assistant_threads_setSuggestedPrompts().""" - client = MagicMock(spec=WebClient) - client.assistant_threads_setSuggestedPrompts.return_value = MagicMock() - - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - agent.set_suggested_prompts(prompts=["Hello"], token="xoxb-override") - - client.assistant_threads_setSuggestedPrompts.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - prompts=[{"title": "Hello", "message": "Hello"}], - title=None, - token="xoxb-override", - ) - - def test_set_suggested_prompts_requires_prompts(self): - """set_suggested_prompts() raises TypeError when prompts is not provided.""" - client = MagicMock(spec=WebClient) - agent = BoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - with pytest.raises(TypeError): - agent.set_suggested_prompts() - - def test_import_from_slack_bolt(self): - from slack_bolt import BoltAgent as ImportedBoltAgent - - assert ImportedBoltAgent is BoltAgent - - def test_import_from_agent_module(self): - from slack_bolt.agent import BoltAgent as ImportedBoltAgent - - assert ImportedBoltAgent is BoltAgent diff --git a/tests/slack_bolt_async/agent/__init__.py b/tests/slack_bolt_async/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/slack_bolt_async/agent/test_async_agent.py b/tests/slack_bolt_async/agent/test_async_agent.py deleted file mode 100644 index 3ed8ef0b4..000000000 --- a/tests/slack_bolt_async/agent/test_async_agent.py +++ /dev/null @@ -1,399 +0,0 @@ -from unittest.mock import MagicMock - -import pytest -from slack_sdk.web.async_client import AsyncWebClient -from slack_sdk.web.async_chat_stream import AsyncChatStream - -from slack_bolt.agent.async_agent import AsyncBoltAgent - - -def _make_async_chat_stream_mock(): - mock_stream = MagicMock(spec=AsyncChatStream) - call_tracker = MagicMock() - - async def fake_chat_stream(**kwargs): - call_tracker(**kwargs) - return mock_stream - - return fake_chat_stream, call_tracker, mock_stream - - -def _make_async_api_mock(): - mock_response = MagicMock() - call_tracker = MagicMock() - - async def fake_api_call(**kwargs): - call_tracker(**kwargs) - return mock_response - - return fake_api_call, call_tracker, mock_response - - -class TestAsyncBoltAgent: - @pytest.mark.asyncio - async def test_chat_stream_uses_context_defaults(self): - """AsyncBoltAgent.chat_stream() passes context defaults to AsyncWebClient.chat_stream().""" - client = MagicMock(spec=AsyncWebClient) - client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - stream = await agent.chat_stream() - - call_tracker.assert_called_once_with( - channel="C111", - thread_ts="1234567890.123456", - recipient_team_id="T111", - recipient_user_id="W222", - ) - assert stream is not None - - @pytest.mark.asyncio - async def test_chat_stream_overrides_context_defaults(self): - """Explicit kwargs to chat_stream() override context defaults.""" - client = MagicMock(spec=AsyncWebClient) - client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - stream = await agent.chat_stream( - channel="C999", - thread_ts="9999999999.999999", - recipient_team_id="T999", - recipient_user_id="U999", - ) - - call_tracker.assert_called_once_with( - channel="C999", - thread_ts="9999999999.999999", - recipient_team_id="T999", - recipient_user_id="U999", - ) - assert stream is not None - - @pytest.mark.asyncio - async def test_chat_stream_rejects_partial_overrides(self): - """Passing only some of the four context args raises ValueError.""" - client = MagicMock(spec=AsyncWebClient) - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - with pytest.raises(ValueError, match="Either provide all of"): - await agent.chat_stream(channel="C999") - - @pytest.mark.asyncio - async def test_chat_stream_passes_extra_kwargs(self): - """Extra kwargs are forwarded to AsyncWebClient.chat_stream().""" - client = MagicMock(spec=AsyncWebClient) - client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.chat_stream(buffer_size=512) - - call_tracker.assert_called_once_with( - channel="C111", - thread_ts="1234567890.123456", - recipient_team_id="T111", - recipient_user_id="W222", - buffer_size=512, - ) - - @pytest.mark.asyncio - async def test_chat_stream_falls_back_to_ts(self): - """When thread_ts is not set, chat_stream() falls back to ts.""" - client = MagicMock(spec=AsyncWebClient) - client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - team_id="T111", - ts="1111111111.111111", - user_id="W222", - ) - stream = await agent.chat_stream() - - call_tracker.assert_called_once_with( - channel="C111", - thread_ts="1111111111.111111", - recipient_team_id="T111", - recipient_user_id="W222", - ) - assert stream is not None - - @pytest.mark.asyncio - async def test_chat_stream_prefers_thread_ts_over_ts(self): - """thread_ts takes priority over ts.""" - client = MagicMock(spec=AsyncWebClient) - client.chat_stream, call_tracker, _ = _make_async_chat_stream_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - team_id="T111", - thread_ts="1234567890.123456", - ts="1111111111.111111", - user_id="W222", - ) - stream = await agent.chat_stream() - - call_tracker.assert_called_once_with( - channel="C111", - thread_ts="1234567890.123456", - recipient_team_id="T111", - recipient_user_id="W222", - ) - assert stream is not None - - @pytest.mark.asyncio - async def test_set_status_uses_context_defaults(self): - """AsyncBoltAgent.set_status() passes context defaults to AsyncWebClient.assistant_threads_setStatus().""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_status(status="Thinking...") - - call_tracker.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - status="Thinking...", - loading_messages=None, - ) - - @pytest.mark.asyncio - async def test_set_status_with_loading_messages(self): - """AsyncBoltAgent.set_status() forwards loading_messages.""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_status( - status="Thinking...", - loading_messages=["Sitting...", "Waiting..."], - ) - - call_tracker.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - status="Thinking...", - loading_messages=["Sitting...", "Waiting..."], - ) - - @pytest.mark.asyncio - async def test_set_status_overrides_context_defaults(self): - """Explicit channel_id/thread_ts override context defaults.""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_status( - status="Thinking...", - channel_id="C999", - thread_ts="9999999999.999999", - ) - - call_tracker.assert_called_once_with( - channel_id="C999", - thread_ts="9999999999.999999", - status="Thinking...", - loading_messages=None, - ) - - @pytest.mark.asyncio - async def test_set_status_passes_extra_kwargs(self): - """Extra kwargs are forwarded to AsyncWebClient.assistant_threads_setStatus().""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setStatus, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_status(status="Thinking...", token="xoxb-override") - - call_tracker.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - status="Thinking...", - loading_messages=None, - token="xoxb-override", - ) - - @pytest.mark.asyncio - async def test_set_status_requires_status(self): - """set_status() raises TypeError when status is not provided.""" - client = MagicMock(spec=AsyncWebClient) - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - with pytest.raises(TypeError): - await agent.set_status() - - @pytest.mark.asyncio - async def test_set_suggested_prompts_uses_context_defaults(self): - """AsyncBoltAgent.set_suggested_prompts() passes context defaults to AsyncWebClient.assistant_threads_setSuggestedPrompts().""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_suggested_prompts(prompts=["What can you do?", "Help me write code"]) - - call_tracker.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - prompts=[ - {"title": "What can you do?", "message": "What can you do?"}, - {"title": "Help me write code", "message": "Help me write code"}, - ], - title=None, - ) - - @pytest.mark.asyncio - async def test_set_suggested_prompts_with_dict_prompts(self): - """AsyncBoltAgent.set_suggested_prompts() accepts dict prompts with title and message.""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_suggested_prompts( - prompts=[ - {"title": "Short title", "message": "A much longer message for this prompt"}, - ], - title="Suggestions", - ) - - call_tracker.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - prompts=[ - {"title": "Short title", "message": "A much longer message for this prompt"}, - ], - title="Suggestions", - ) - - @pytest.mark.asyncio - async def test_set_suggested_prompts_overrides_context_defaults(self): - """Explicit channel_id/thread_ts override context defaults.""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_suggested_prompts( - prompts=["Hello"], - channel_id="C999", - thread_ts="9999999999.999999", - ) - - call_tracker.assert_called_once_with( - channel_id="C999", - thread_ts="9999999999.999999", - prompts=[{"title": "Hello", "message": "Hello"}], - title=None, - ) - - @pytest.mark.asyncio - async def test_set_suggested_prompts_passes_extra_kwargs(self): - """Extra kwargs are forwarded to AsyncWebClient.assistant_threads_setSuggestedPrompts().""" - client = MagicMock(spec=AsyncWebClient) - client.assistant_threads_setSuggestedPrompts, call_tracker, _ = _make_async_api_mock() - - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - await agent.set_suggested_prompts(prompts=["Hello"], token="xoxb-override") - - call_tracker.assert_called_once_with( - channel_id="C111", - thread_ts="1234567890.123456", - prompts=[{"title": "Hello", "message": "Hello"}], - title=None, - token="xoxb-override", - ) - - @pytest.mark.asyncio - async def test_set_suggested_prompts_requires_prompts(self): - """set_suggested_prompts() raises TypeError when prompts is not provided.""" - client = MagicMock(spec=AsyncWebClient) - agent = AsyncBoltAgent( - client=client, - channel_id="C111", - thread_ts="1234567890.123456", - team_id="T111", - user_id="W222", - ) - with pytest.raises(TypeError): - await agent.set_suggested_prompts() - - @pytest.mark.asyncio - async def test_import_from_agent_module(self): - from slack_bolt.agent.async_agent import AsyncBoltAgent as ImportedAsyncBoltAgent - - assert ImportedAsyncBoltAgent is AsyncBoltAgent From 6e57716ad35e2e9ef229a1276c42890ae4b10428 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 23 Mar 2026 07:34:02 -0700 Subject: [PATCH 203/282] chore: replace sleep-based polling with Event synchronization in tests (#1467) --- tests/scenario_tests/test_events_assistant.py | 99 ++++++------- ...est_events_assistant_without_middleware.py | 59 ++++---- .../scenario_tests/test_events_say_stream.py | 72 ++++------ .../test_events_assistant.py | 133 ++++++++---------- ...est_events_assistant_without_middleware.py | 74 +++++----- .../test_events_say_stream.py | 71 ++++------ 6 files changed, 222 insertions(+), 286 deletions(-) diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index 5bc270d86..a970c9fa4 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -1,27 +1,16 @@ -import time -from time import sleep +from threading import Event from typing import Callable from slack_sdk.web import WebClient -from slack_bolt import App, BoltRequest, Assistant, Say, SetSuggestedPrompts, SetStatus, BoltContext +from slack_bolt import App, Assistant, BoltContext, BoltRequest, Say, SetStatus, SetSuggestedPrompts from slack_bolt.middleware import Middleware from slack_bolt.request import BoltRequest as BoltRequestType from slack_bolt.response import BoltResponse -from tests.mock_web_api_server import ( - setup_mock_web_api_server, - cleanup_mock_web_api_server, -) +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server from tests.utils import remove_os_env_temporarily, restore_os_env -def assert_target_called(called: dict, timeout: float = 0.5): - deadline = time.time() + timeout - while called["value"] is not True and time.time() < deadline: - time.sleep(0.1) - assert called["value"] is True - - class TestEventsAssistant: valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" @@ -41,7 +30,7 @@ def teardown_method(self): def test_thread_started(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.thread_started def start_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts, set_status: SetStatus, context: BoltContext): @@ -54,37 +43,37 @@ def start_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts, set_statu set_suggested_prompts( prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}], title="foo" ) - called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=thread_started_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_thread_context_changed(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.thread_context_changed def handle_thread_context_changed(context: BoltContext): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" - called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=thread_context_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_user_message(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.user_message def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): @@ -94,7 +83,7 @@ def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): try: set_status("is typing...") say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: say(f"Oops, something went wrong (error: {e})") @@ -103,12 +92,12 @@ def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): request = BoltRequest(body=user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_user_message_with_assistant_thread(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.user_message def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): @@ -118,7 +107,7 @@ def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): try: set_status("is typing...") say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: say(f"Oops, something went wrong (error: {e})") @@ -127,77 +116,77 @@ def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): request = BoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_message_changed(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.user_message def handle_user_message(): - called["value"] = True + listener_called.set() @assistant.bot_message def handle_bot_message(): - called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert called["value"] is False + assert listener_called.wait(timeout=0.1) is False def test_channel_user_message_ignored(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.user_message def handle_user_message(): - called["value"] = True + listener_called.set() @assistant.bot_message def handle_bot_message(): - called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 404 - assert called["value"] is False + assert listener_called.wait(timeout=0.1) is False def test_channel_message_changed_ignored(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.user_message def handle_user_message(): - called["value"] = True + listener_called.set() @assistant.bot_message def handle_bot_message(): - called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=channel_message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 404 - assert called["value"] is False + assert listener_called.wait(timeout=0.1) is False def test_assistant_with_custom_listener_middleware(self): app = App(client=self.web_client) assistant = Assistant() - handler_called = {"value": False} - middleware_called = {"value": False} + listener_called = Event() + middleware_called = Event() class TestMiddleware(Middleware): def process(self, *, req: BoltRequestType, resp: BoltResponse, next: Callable[[], BoltResponse]): - middleware_called["value"] = True + middleware_called.set() # Verify assistant utilities are available assert req.context.get("set_status") is not None assert req.context.get("set_title") is not None @@ -208,52 +197,52 @@ def process(self, *, req: BoltRequestType, resp: BoltResponse, next: Callable[[] @assistant.thread_started(middleware=[TestMiddleware()]) def start_thread(): - handler_called["value"] = True + listener_called.set() @assistant.user_message(middleware=[TestMiddleware()]) def handle_user_message(): - handler_called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=thread_started_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(handler_called) - assert_target_called(middleware_called) + assert listener_called.wait(timeout=0.1) is True + assert middleware_called.wait(timeout=0.1) is True - handler_called = {"value": False} - middleware_called = {"value": False} + listener_called.clear() + middleware_called.clear() request = BoltRequest(body=user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(handler_called) - assert_target_called(middleware_called) + assert listener_called.wait(timeout=0.1) is True + assert middleware_called.wait(timeout=0.1) is True def test_assistant_custom_middleware_can_short_circuit(self): app = App(client=self.web_client) assistant = Assistant() - handler_called = {"value": False} - middleware_called = {"value": False} + listener_called = Event() + middleware_called = Event() class BlockingMiddleware(Middleware): def process(self, *, req: BoltRequestType, resp: BoltResponse, next: Callable[[], BoltResponse]): - middleware_called["value"] = True + middleware_called.set() # Intentionally not calling next() to short-circuit return BoltResponse(status=200) @assistant.thread_started(middleware=[BlockingMiddleware()]) def start_thread(say: Say, context: BoltContext): - handler_called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=thread_started_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(middleware_called) - assert handler_called["value"] is False + assert middleware_called.wait(timeout=0.1) is True + assert listener_called.wait(timeout=0.1) is False def build_payload(event: dict) -> dict: diff --git a/tests/scenario_tests/test_events_assistant_without_middleware.py b/tests/scenario_tests/test_events_assistant_without_middleware.py index 6a9381a33..c95f16f99 100644 --- a/tests/scenario_tests/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests/test_events_assistant_without_middleware.py @@ -1,14 +1,11 @@ +from threading import Event + from slack_sdk.web import WebClient -from slack_bolt import App, BoltRequest, Say, SetStatus, SetTitle, SaveThreadContext, BoltContext +from slack_bolt import App, BoltContext, BoltRequest, SaveThreadContext, Say, SetStatus, SetSuggestedPrompts, SetTitle from slack_bolt.context.get_thread_context.get_thread_context import GetThreadContext -from slack_bolt.context.set_suggested_prompts.set_suggested_prompts import SetSuggestedPrompts -from tests.mock_web_api_server import ( - setup_mock_web_api_server, - cleanup_mock_web_api_server, -) +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server from tests.scenario_tests.test_events_assistant import ( - assert_target_called, channel_message_changed_event_body, channel_user_message_event_body, message_changed_event_body, @@ -38,7 +35,7 @@ def teardown_method(self): def test_thread_started(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("assistant_thread_started") def handle_assistant_thread_started( @@ -60,16 +57,16 @@ def handle_assistant_thread_started( assert save_thread_context is not None say("Hi, how can I help you today?") set_suggested_prompts(prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}]) - called["value"] = True + listener_called.set() request = BoltRequest(body=thread_started_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_thread_context_changed(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("assistant_thread_context_changed") def handle_assistant_thread_context_changed( @@ -89,16 +86,16 @@ def handle_assistant_thread_context_changed( assert set_suggested_prompts is not None assert get_thread_context is not None assert save_thread_context is not None - called["value"] = True + listener_called.set() request = BoltRequest(body=thread_context_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_user_message(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.message("") def handle_message( @@ -121,18 +118,18 @@ def handle_message( try: set_status("is typing...") say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: say(f"Oops, something went wrong (error: {e})") request = BoltRequest(body=user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_user_message_with_assistant_thread(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.message("") def handle_message( @@ -155,18 +152,18 @@ def handle_message( try: set_status("is typing...") say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: say(f"Oops, something went wrong (error: {e})") request = BoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_message_changed(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("message") def handle_message_event( @@ -185,16 +182,16 @@ def handle_message_event( assert set_suggested_prompts is None assert get_thread_context is None assert save_thread_context is None - called["value"] = True + listener_called.set() request = BoltRequest(body=message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_channel_user_message(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("message") def handle_message_event( @@ -213,16 +210,16 @@ def handle_message_event( assert set_suggested_prompts is None assert get_thread_context is None assert save_thread_context is None - called["value"] = True + listener_called.set() request = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_channel_message_changed(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("message") def handle_message_event( @@ -241,17 +238,17 @@ def handle_message_event( assert set_suggested_prompts is None assert get_thread_context is None assert save_thread_context is None - called["value"] = True + listener_called.set() request = BoltRequest(body=channel_message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_assistant_events_agent_kwargs_disabled(self): app = App(client=self.web_client, attaching_agent_kwargs_enabled=False) - called = {"value": False} + listener_called = Event() @app.event("assistant_thread_started") def start_thread(context: BoltContext): @@ -260,9 +257,9 @@ def start_thread(context: BoltContext): assert context.get("set_suggested_prompts") is None assert context.get("get_thread_context") is None assert context.get("save_thread_context") is None - called["value"] = True + listener_called.set() request = BoltRequest(body=thread_started_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True diff --git a/tests/scenario_tests/test_events_say_stream.py b/tests/scenario_tests/test_events_say_stream.py index 75b0c612c..e0ab66aab 100644 --- a/tests/scenario_tests/test_events_say_stream.py +++ b/tests/scenario_tests/test_events_say_stream.py @@ -1,33 +1,19 @@ import json -import time +from threading import Event from urllib.parse import quote from slack_sdk.web import WebClient -from slack_bolt import App, BoltRequest, BoltContext -from slack_bolt.context.say_stream.say_stream import SayStream -from slack_bolt.middleware.assistant import Assistant -from tests.mock_web_api_server import ( - setup_mock_web_api_server, - cleanup_mock_web_api_server, -) +from slack_bolt import App, Assistant, BoltContext, BoltRequest, SayStream +from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server from tests.scenario_tests.test_app import app_mention_event_body -from tests.scenario_tests.test_events_assistant import ( - thread_started_event_body, - user_message_event_body as threaded_user_message_event_body, -) +from tests.scenario_tests.test_events_assistant import thread_started_event_body +from tests.scenario_tests.test_events_assistant import user_message_event_body as threaded_user_message_event_body from tests.scenario_tests.test_message_bot import bot_message_event_payload, user_message_event_payload from tests.scenario_tests.test_view_submission import body as view_submission_body from tests.utils import remove_os_env_temporarily, restore_os_env -def assert_target_called(called: dict, timeout: float = 1.0): - deadline = time.time() + timeout - while called["value"] is not True and time.time() < deadline: - time.sleep(0.1) - assert called["value"] is True - - class TestEventsSayStream: valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" @@ -46,7 +32,7 @@ def teardown_method(self): def test_say_stream_injected_for_app_mention(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("app_mention") def handle_mention(say_stream: SayStream, context: BoltContext): @@ -57,16 +43,16 @@ def handle_mention(say_stream: SayStream, context: BoltContext): assert say_stream.thread_ts == "1595926230.009600" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = BoltRequest(body=app_mention_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_say_stream_with_org_level_install(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("app_mention") def handle_mention(say_stream: SayStream, context: BoltContext): @@ -75,16 +61,16 @@ def handle_mention(say_stream: SayStream, context: BoltContext): assert say_stream is not None assert isinstance(say_stream, SayStream) assert say_stream.recipient_team_id == "E111" - called["value"] = True + listener_called.set() request = BoltRequest(body=org_app_mention_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_say_stream_injected_for_threaded_message(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.event("message") def handle_message(say_stream: SayStream, context: BoltContext): @@ -95,16 +81,16 @@ def handle_message(say_stream: SayStream, context: BoltContext): assert say_stream.thread_ts == "1726133698.626339" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = BoltRequest(body=threaded_user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_say_stream_in_user_message(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.message("") def handle_user_message(say_stream: SayStream, context: BoltContext): @@ -115,16 +101,16 @@ def handle_user_message(say_stream: SayStream, context: BoltContext): assert say_stream.thread_ts == "1610261659.001400" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = BoltRequest(body=user_message_event_payload, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_say_stream_in_bot_message(self): app = App(client=self.web_client) - called = {"value": False} + listener_called = Event() @app.message("") def handle_bot_message(say_stream: SayStream, context: BoltContext): @@ -135,17 +121,17 @@ def handle_bot_message(say_stream: SayStream, context: BoltContext): assert say_stream.thread_ts == "1610261539.000900" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = BoltRequest(body=bot_message_event_payload, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_say_stream_in_assistant_thread_started(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.thread_started def start_thread(say_stream: SayStream, context: BoltContext): @@ -156,19 +142,19 @@ def start_thread(say_stream: SayStream, context: BoltContext): assert say_stream.thread_ts == "1726133698.626339" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=thread_started_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_say_stream_in_assistant_user_message(self): app = App(client=self.web_client) assistant = Assistant() - called = {"value": False} + listener_called = Event() @assistant.user_message def handle_user_message(say_stream: SayStream, context: BoltContext): @@ -179,32 +165,32 @@ def handle_user_message(say_stream: SayStream, context: BoltContext): assert say_stream.thread_ts == "1726133698.626339" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() app.assistant(assistant) request = BoltRequest(body=threaded_user_message_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True def test_say_stream_is_none_for_view_submission(self): app = App(client=self.web_client, request_verification_enabled=False) - called = {"value": False} + listener_called = Event() @app.view("view-id") def handle_view(ack, say_stream, context: BoltContext): ack() assert say_stream is None assert context.say_stream is None - called["value"] = True + listener_called.set() request = BoltRequest( body=f"payload={quote(json.dumps(view_submission_body))}", ) response = app.dispatch(request) assert response.status == 200 - assert_target_called(called) + assert listener_called.wait(timeout=0.1) is True org_app_mention_event_body = { diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index 87b337536..9b2e43eb1 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -1,33 +1,24 @@ import asyncio -import time from typing import Awaitable, Callable, Optional import pytest from slack_sdk.web.async_client import AsyncWebClient -from slack_bolt.app.async_app import AsyncApp -from slack_bolt.context.async_context import AsyncBoltContext -from slack_bolt.context.say.async_say import AsyncSay -from slack_bolt.context.set_status.async_set_status import AsyncSetStatus -from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts -from slack_bolt.middleware.assistant.async_assistant import AsyncAssistant +from slack_bolt.async_app import ( + AsyncApp, + AsyncAssistant, + AsyncBoltContext, + AsyncBoltRequest, + AsyncSay, + AsyncSetStatus, + AsyncSetSuggestedPrompts, +) from slack_bolt.middleware.async_middleware import AsyncMiddleware -from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse -from tests.mock_web_api_server import ( - cleanup_mock_web_api_server_async, - setup_mock_web_api_server_async, -) +from tests.mock_web_api_server import cleanup_mock_web_api_server_async, setup_mock_web_api_server_async from tests.utils import remove_os_env_temporarily, restore_os_env -async def assert_target_called(called: dict, timeout: float = 0.5): - deadline = time.time() + timeout - while called["value"] is not True and time.time() < deadline: - await asyncio.sleep(0.1) - assert called["value"] is True - - class TestAsyncEventsAssistant: valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" @@ -51,7 +42,7 @@ def setup_teardown(self): async def test_thread_started(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.thread_started async def start_thread( @@ -72,39 +63,39 @@ async def start_thread( prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}], title="foo", ) - called["value"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_thread_context_changed(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.thread_context_changed async def handle_thread_context_changed(context: AsyncBoltContext): assert context.channel_id == "D111" assert context.thread_ts == "1726133698.626339" - called["value"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=thread_context_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_user_message(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.user_message async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context: AsyncBoltContext): @@ -114,7 +105,7 @@ async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context try: await set_status("is typing...") await say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: await say(f"Oops, something went wrong (error: {e})") @@ -123,13 +114,13 @@ async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_user_message_with_assistant_thread(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.user_message async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context: AsyncBoltContext): @@ -139,7 +130,7 @@ async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context try: await set_status("is typing...") await say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: await say(f"Oops, something went wrong (error: {e})") @@ -148,84 +139,78 @@ async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context request = AsyncBoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_message_changed(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.user_message async def handle_user_message(): - called["value"] = True + listener_called.set() @assistant.bot_message async def handle_bot_message(): - called["value"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - assert called["value"] is False + await asyncio.sleep(0.1) + assert not listener_called.is_set() @pytest.mark.asyncio async def test_channel_user_message_ignored(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.user_message async def handle_user_message(): - called["value"] = True + listener_called.set() @assistant.bot_message async def handle_bot_message(): - called["value"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=channel_user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 404 - assert called["value"] is False + await asyncio.sleep(0.1) + assert not listener_called.is_set() @pytest.mark.asyncio async def test_channel_message_changed_ignored(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.user_message async def handle_user_message(): - called["value"] = True + listener_called.set() @assistant.bot_message async def handle_bot_message(): - called["value"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=channel_message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 404 - assert called["value"] is False + await asyncio.sleep(0.1) + assert not listener_called.is_set() @pytest.mark.asyncio async def test_assistant_events_kwargs_disabled(self): app = AsyncApp(client=self.web_client, attaching_agent_kwargs_enabled=False) - - state = {"called": False} - - async def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - await asyncio.sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False + listener_called = asyncio.Event() @app.event("assistant_thread_started") async def start_thread(context: AsyncBoltContext): @@ -234,27 +219,19 @@ async def start_thread(context: AsyncBoltContext): assert context.get("set_suggested_prompts") is None assert context.get("get_thread_context") is None assert context.get("save_thread_context") is None - state["called"] = True + listener_called.set() request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called() + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_assistant_with_custom_listener_middleware(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - - state = {"called": False, "middleware_called": False} - - async def assert_target_called(): - count = 0 - while state["called"] is False and count < 20: - await asyncio.sleep(0.1) - count += 1 - assert state["called"] is True - state["called"] = False + listener_called = asyncio.Event() + middleware_called = asyncio.Event() class TestAsyncMiddleware(AsyncMiddleware): async def async_process( @@ -264,7 +241,7 @@ async def async_process( resp: BoltResponse, next: Callable[[], Awaitable[BoltResponse]], ) -> Optional[BoltResponse]: - state["middleware_called"] = True + middleware_called.set() # Verify assistant utilities are available (set by _AsyncAssistantMiddleware before this) assert req.context.get("set_status") is not None assert req.context.get("set_title") is not None @@ -282,7 +259,7 @@ async def start_thread(say: AsyncSay, set_suggested_prompts: AsyncSetSuggestedPr await set_suggested_prompts( prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}] ) - state["called"] = True + listener_called.set() @assistant.user_message(middleware=[TestAsyncMiddleware()]) async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context: AsyncBoltContext): @@ -291,29 +268,30 @@ async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context assert say.thread_ts == context.thread_ts await set_status("is typing...") await say("Here you are!") - state["called"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called() - assert state["middleware_called"] is True - state["middleware_called"] = False + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True + assert (await asyncio.wait_for(middleware_called.wait(), timeout=0.1)) is True + + listener_called.clear() + middleware_called.clear() request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called() - assert state["middleware_called"] is True + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True + assert (await asyncio.wait_for(middleware_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_assistant_custom_middleware_can_short_circuit(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - - state = {"handler_called": False} + listener_called = asyncio.Event() class BlockingAsyncMiddleware(AsyncMiddleware): async def async_process( @@ -328,14 +306,15 @@ async def async_process( @assistant.thread_started(middleware=[BlockingAsyncMiddleware()]) async def start_thread(say: AsyncSay, context: AsyncBoltContext): - state["handler_called"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - assert state["handler_called"] is False + await asyncio.sleep(0.1) + assert not listener_called.is_set() def build_payload(event: dict) -> dict: diff --git a/tests/scenario_tests_async/test_events_assistant_without_middleware.py b/tests/scenario_tests_async/test_events_assistant_without_middleware.py index 916dfd467..4e82cb2c1 100644 --- a/tests/scenario_tests_async/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests_async/test_events_assistant_without_middleware.py @@ -1,21 +1,21 @@ +import asyncio + import pytest from slack_sdk.web.async_client import AsyncWebClient -from slack_bolt.app.async_app import AsyncApp -from slack_bolt.context.async_context import AsyncBoltContext -from slack_bolt.context.say.async_say import AsyncSay -from slack_bolt.context.set_status.async_set_status import AsyncSetStatus -from slack_bolt.context.set_title.async_set_title import AsyncSetTitle -from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts -from slack_bolt.context.get_thread_context.async_get_thread_context import AsyncGetThreadContext -from slack_bolt.context.save_thread_context.async_save_thread_context import AsyncSaveThreadContext -from slack_bolt.request.async_request import AsyncBoltRequest -from tests.mock_web_api_server import ( - cleanup_mock_web_api_server_async, - setup_mock_web_api_server_async, +from slack_bolt.async_app import ( + AsyncApp, + AsyncBoltContext, + AsyncBoltRequest, + AsyncGetThreadContext, + AsyncSaveThreadContext, + AsyncSay, + AsyncSetStatus, + AsyncSetSuggestedPrompts, + AsyncSetTitle, ) +from tests.mock_web_api_server import cleanup_mock_web_api_server_async, setup_mock_web_api_server_async from tests.scenario_tests_async.test_events_assistant import ( - assert_target_called, channel_message_changed_event_body, channel_user_message_event_body, message_changed_event_body, @@ -49,7 +49,7 @@ def setup_teardown(self): @pytest.mark.asyncio async def test_thread_started(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("assistant_thread_started") async def handle_assistant_thread_started( @@ -73,17 +73,17 @@ async def handle_assistant_thread_started( await set_suggested_prompts( prompts=[{"title": "What does SLACK stand for?", "message": "What does SLACK stand for?"}] ) - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_thread_context_changed(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("assistant_thread_context_changed") async def handle_assistant_thread_context_changed( @@ -103,17 +103,17 @@ async def handle_assistant_thread_context_changed( assert set_suggested_prompts is not None assert get_thread_context is not None assert save_thread_context is not None - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=thread_context_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_user_message(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.message("") async def handle_message( @@ -136,19 +136,19 @@ async def handle_message( try: await set_status("is typing...") await say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: await say(f"Oops, something went wrong (error: {e})") request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_user_message_with_assistant_thread(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.message("") async def handle_message( @@ -171,19 +171,19 @@ async def handle_message( try: await set_status("is typing...") await say("Here you are!") - called["value"] = True + listener_called.set() except Exception as e: await say(f"Oops, something went wrong (error: {e})") request = AsyncBoltRequest(body=user_message_event_body_with_assistant_thread, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_message_changed(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("message") async def handle_message_event( @@ -202,17 +202,17 @@ async def handle_message_event( assert set_suggested_prompts is None assert get_thread_context is None assert save_thread_context is None - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_channel_user_message(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("message") async def handle_message_event( @@ -231,17 +231,17 @@ async def handle_message_event( assert set_suggested_prompts is None assert get_thread_context is None assert save_thread_context is None - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=channel_user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_channel_message_changed(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("message") async def handle_message_event( @@ -260,18 +260,18 @@ async def handle_message_event( assert set_suggested_prompts is None assert get_thread_context is None assert save_thread_context is None - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=channel_message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_assistant_events_agent_kwargs_disabled(self): app = AsyncApp(client=self.web_client, attaching_agent_kwargs_enabled=False) - called = {"value": False} + listener_called = asyncio.Event() @app.event("assistant_thread_started") async def start_thread(context: AsyncBoltContext): @@ -280,9 +280,9 @@ async def start_thread(context: AsyncBoltContext): assert context.get("set_suggested_prompts") is None assert context.get("get_thread_context") is None assert context.get("save_thread_context") is None - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True diff --git a/tests/scenario_tests_async/test_events_say_stream.py b/tests/scenario_tests_async/test_events_say_stream.py index c24bc7bfc..6abcfad88 100644 --- a/tests/scenario_tests_async/test_events_say_stream.py +++ b/tests/scenario_tests_async/test_events_say_stream.py @@ -1,35 +1,20 @@ import asyncio import json -import time from urllib.parse import quote import pytest from slack_sdk.web.async_client import AsyncWebClient -from slack_bolt.app.async_app import AsyncApp -from slack_bolt.async_app import AsyncAssistant -from slack_bolt.context.async_context import AsyncBoltContext -from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream -from slack_bolt.request.async_request import AsyncBoltRequest -from tests.mock_web_api_server import ( - cleanup_mock_web_api_server_async, - setup_mock_web_api_server_async, -) +from slack_bolt.async_app import AsyncApp, AsyncAssistant, AsyncBoltContext, AsyncBoltRequest, AsyncSayStream +from tests.mock_web_api_server import cleanup_mock_web_api_server_async, setup_mock_web_api_server_async from tests.scenario_tests_async.test_app import app_mention_event_body +from tests.scenario_tests_async.test_events_assistant import thread_started_event_body from tests.scenario_tests_async.test_events_assistant import user_message_event_body as threaded_user_message_event_body -from tests.scenario_tests_async.test_events_assistant import thread_started_event_body, user_message_event_body from tests.scenario_tests_async.test_message_bot import bot_message_event_payload, user_message_event_payload from tests.scenario_tests_async.test_view_submission import body as view_submission_body from tests.utils import remove_os_env_temporarily, restore_os_env -async def assert_target_called(called: dict, timeout: float = 0.5): - deadline = time.time() + timeout - while called["value"] is not True and time.time() < deadline: - await asyncio.sleep(0.1) - assert called["value"] is True - - class TestAsyncEventsSayStream: valid_token = "xoxb-valid" mock_api_server_base_url = "http://localhost:8888" @@ -51,7 +36,7 @@ def setup_teardown(self): @pytest.mark.asyncio async def test_say_stream_injected_for_app_mention(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("app_mention") async def handle_mention(say_stream: AsyncSayStream, context: AsyncBoltContext): @@ -62,17 +47,17 @@ async def handle_mention(say_stream: AsyncSayStream, context: AsyncBoltContext): assert say_stream.thread_ts == "1595926230.009600" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=app_mention_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_say_stream_with_org_level_install(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("app_mention") async def handle_mention(say_stream: AsyncSayStream, context: AsyncBoltContext): @@ -81,17 +66,17 @@ async def handle_mention(say_stream: AsyncSayStream, context: AsyncBoltContext): assert say_stream is not None assert isinstance(say_stream, AsyncSayStream) assert say_stream.recipient_team_id == "E111" - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=org_app_mention_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_say_stream_injected_for_threaded_message(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.event("message") async def handle_message(say_stream: AsyncSayStream, context: AsyncBoltContext): @@ -102,17 +87,17 @@ async def handle_message(say_stream: AsyncSayStream, context: AsyncBoltContext): assert say_stream.thread_ts == "1726133698.626339" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=threaded_user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_say_stream_in_user_message(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.message("") async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltContext): @@ -123,17 +108,17 @@ async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltCont assert say_stream.thread_ts == "1610261659.001400" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=user_message_event_payload, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_say_stream_in_bot_message(self): app = AsyncApp(client=self.web_client) - called = {"value": False} + listener_called = asyncio.Event() @app.message("") async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltContext): @@ -144,18 +129,18 @@ async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltCont assert say_stream.thread_ts == "1610261539.000900" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() request = AsyncBoltRequest(body=bot_message_event_payload, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_say_stream_in_assistant_thread_started(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.thread_started async def start_thread(say_stream: AsyncSayStream, context: AsyncBoltContext): @@ -166,20 +151,20 @@ async def start_thread(say_stream: AsyncSayStream, context: AsyncBoltContext): assert say_stream.thread_ts == "1726133698.626339" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() app.assistant(assistant) request = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_say_stream_in_assistant_user_message(self): app = AsyncApp(client=self.web_client) assistant = AsyncAssistant() - called = {"value": False} + listener_called = asyncio.Event() @assistant.user_message async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltContext): @@ -190,33 +175,33 @@ async def handle_user_message(say_stream: AsyncSayStream, context: AsyncBoltCont assert say_stream.thread_ts == "1726133698.626339" assert say_stream.recipient_team_id == context.team_id assert say_stream.recipient_user_id == context.user_id - called["value"] = True + listener_called.set() app.assistant(assistant) - request = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") + request = AsyncBoltRequest(body=threaded_user_message_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio async def test_say_stream_is_none_for_view_submission(self): app = AsyncApp(client=self.web_client, request_verification_enabled=False) - called = {"value": False} + listener_called = asyncio.Event() @app.view("view-id") async def handle_view(ack, say_stream, context: AsyncBoltContext): await ack() assert say_stream is None assert context.say_stream is None - called["value"] = True + listener_called.set() request = AsyncBoltRequest( body=f"payload={quote(json.dumps(view_submission_body))}", ) response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called(called) + assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True org_app_mention_event_body = { From 98a8f593c7b4cde4834338d5c0ce89686c7168cf Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 23 Mar 2026 09:07:40 -0700 Subject: [PATCH 204/282] chore: fix test warnings across test suite (#1468) --- pyproject.toml | 6 +----- tests/adapter_tests/django/conftest.py | 6 ++++++ tests/adapter_tests/django/test_django.py | 2 -- tests/adapter_tests/starlette/test_fastapi.py | 8 ++++---- tests/adapter_tests/starlette/test_starlette.py | 6 +++--- tests/adapter_tests_async/test_async_fastapi.py | 8 ++++---- tests/adapter_tests_async/test_async_starlette.py | 6 +++--- tests/scenario_tests/test_app.py | 10 +++++----- tests/scenario_tests/test_lazy.py | 4 ++-- tests/scenario_tests_async/test_app.py | 10 +++++----- tests/scenario_tests_async/test_lazy.py | 4 ++-- 11 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 tests/adapter_tests/django/conftest.py diff --git a/pyproject.toml b/pyproject.toml index a5c12548b..88842d0d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,11 +46,7 @@ log_file = "logs/pytest.log" log_file_level = "DEBUG" log_format = "%(asctime)s %(levelname)s %(message)s" log_date_format = "%Y-%m-%d %H:%M:%S" -filterwarnings = [ - "ignore:\"@coroutine\" decorator is deprecated since Python 3.8, use \"async def\" instead:DeprecationWarning", - "ignore:The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.:DeprecationWarning", - "ignore:Unknown config option. asyncio_mode:pytest.PytestConfigWarning", # ignore warning when asyncio_mode is set but pytest-asyncio is not installed -] +filterwarnings = [] asyncio_mode = "auto" [tool.mypy] diff --git a/tests/adapter_tests/django/conftest.py b/tests/adapter_tests/django/conftest.py new file mode 100644 index 000000000..b2697fe2b --- /dev/null +++ b/tests/adapter_tests/django/conftest.py @@ -0,0 +1,6 @@ +import os + +import django + +os.environ["DJANGO_SETTINGS_MODULE"] = "tests.adapter_tests.django.test_django_settings" +django.setup() diff --git a/tests/adapter_tests/django/test_django.py b/tests/adapter_tests/django/test_django.py index cb14f966d..f31a46411 100644 --- a/tests/adapter_tests/django/test_django.py +++ b/tests/adapter_tests/django/test_django.py @@ -1,5 +1,4 @@ import json -import os from time import time from urllib.parse import quote @@ -29,7 +28,6 @@ class TestDjango(TestCase): base_url=mock_api_server_base_url, ) - os.environ["DJANGO_SETTINGS_MODULE"] = "tests.adapter_tests.django.test_django_settings" rf = RequestFactory() def setUp(self): diff --git a/tests/adapter_tests/starlette/test_fastapi.py b/tests/adapter_tests/starlette/test_fastapi.py index 64e633fe2..f91b9897e 100644 --- a/tests/adapter_tests/starlette/test_fastapi.py +++ b/tests/adapter_tests/starlette/test_fastapi.py @@ -94,7 +94,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -138,7 +138,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -182,7 +182,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -254,7 +254,7 @@ async def endpoint(req: Request, foo: str = Depends(get_foo)): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 diff --git a/tests/adapter_tests/starlette/test_starlette.py b/tests/adapter_tests/starlette/test_starlette.py index 8c6154b3b..18066a9d2 100644 --- a/tests/adapter_tests/starlette/test_starlette.py +++ b/tests/adapter_tests/starlette/test_starlette.py @@ -97,7 +97,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -143,7 +143,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -189,7 +189,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 diff --git a/tests/adapter_tests_async/test_async_fastapi.py b/tests/adapter_tests_async/test_async_fastapi.py index ea9308842..e0175d3fa 100644 --- a/tests/adapter_tests_async/test_async_fastapi.py +++ b/tests/adapter_tests_async/test_async_fastapi.py @@ -94,7 +94,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -138,7 +138,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -182,7 +182,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -255,7 +255,7 @@ async def endpoint(req: Request, foo: str = Depends(get_foo)): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 diff --git a/tests/adapter_tests_async/test_async_starlette.py b/tests/adapter_tests_async/test_async_starlette.py index 7e9a18a58..849c75168 100644 --- a/tests/adapter_tests_async/test_async_starlette.py +++ b/tests/adapter_tests_async/test_async_starlette.py @@ -97,7 +97,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -143,7 +143,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 @@ -189,7 +189,7 @@ async def endpoint(req: Request): client = TestClient(api) response = client.post( "/slack/events", - data=body, + content=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 diff --git a/tests/scenario_tests/test_app.py b/tests/scenario_tests/test_app.py index 9bbad6d9a..9fe6f423f 100644 --- a/tests/scenario_tests/test_app.py +++ b/tests/scenario_tests/test_app.py @@ -1,7 +1,7 @@ import logging import time from concurrent.futures import Executor -from ssl import SSLContext +import ssl import pytest from slack_sdk import WebClient @@ -236,12 +236,12 @@ def test_none_body_no_middleware(self): assert response.body == '{"error": "unhandled request"}' def test_proxy_ssl_for_respond(self): - ssl = SSLContext() + ssl_context = ssl.create_default_context() web_client = WebClient( token=self.valid_token, base_url=self.mock_api_server_base_url, proxy="http://proxy-host:9000/", - ssl=ssl, + ssl=ssl_context, ) app = App( signing_secret="valid", @@ -257,9 +257,9 @@ def test_proxy_ssl_for_respond(self): @app.event("app_mention") def handle(context: BoltContext, respond): assert context.respond.proxy == "http://proxy-host:9000/" - assert context.respond.ssl == ssl + assert context.respond.ssl == ssl_context assert respond.proxy == "http://proxy-host:9000/" - assert respond.ssl == ssl + assert respond.ssl == ssl_context result["called"] = True req = BoltRequest(body=app_mention_event_body, headers={}, mode="socket_mode") diff --git a/tests/scenario_tests/test_lazy.py b/tests/scenario_tests/test_lazy.py index d9e88b280..3b2aefbea 100644 --- a/tests/scenario_tests/test_lazy.py +++ b/tests/scenario_tests/test_lazy.py @@ -156,11 +156,11 @@ def async2(context, say): @app.middleware def set_ssl_context(context, next_): - from ssl import SSLContext + import ssl context["foo"] = "FOO" # This causes an error when starting lazy listener executions - context["ssl_context"] = SSLContext() + context["ssl_context"] = ssl.create_default_context() next_() # 2021-12-13 11:14:29 ERROR Failed to run a middleware middleware (error: cannot pickle 'SSLContext' object) diff --git a/tests/scenario_tests_async/test_app.py b/tests/scenario_tests_async/test_app.py index e27dbd3b3..6f3fb34f8 100644 --- a/tests/scenario_tests_async/test_app.py +++ b/tests/scenario_tests_async/test_app.py @@ -1,6 +1,6 @@ import asyncio import logging -from ssl import SSLContext +import ssl import pytest from slack_sdk import WebClient @@ -185,14 +185,14 @@ def test_installation_store_conflicts(self): @pytest.mark.asyncio async def test_proxy_ssl_for_respond(self): - ssl = SSLContext() + ssl_ctx = ssl.create_default_context() app = AsyncApp( signing_secret="valid", client=AsyncWebClient( token=self.valid_token, base_url=self.mock_api_server_base_url, proxy="http://proxy-host:9000/", - ssl=ssl, + ssl=ssl_ctx, ), authorize=my_authorize, ) @@ -202,9 +202,9 @@ async def test_proxy_ssl_for_respond(self): @app.event("app_mention") async def handle(context: AsyncBoltContext, respond): assert context.respond.proxy == "http://proxy-host:9000/" - assert context.respond.ssl == ssl + assert context.respond.ssl == ssl_ctx assert respond.proxy == "http://proxy-host:9000/" - assert respond.ssl == ssl + assert respond.ssl == ssl_ctx result["called"] = True req = AsyncBoltRequest(body=app_mention_event_body, headers={}, mode="socket_mode") diff --git a/tests/scenario_tests_async/test_lazy.py b/tests/scenario_tests_async/test_lazy.py index 7bf780e08..8c4182f45 100644 --- a/tests/scenario_tests_async/test_lazy.py +++ b/tests/scenario_tests_async/test_lazy.py @@ -138,11 +138,11 @@ async def async2(context, say): @app.middleware async def set_ssl_context(context, next_): - from ssl import SSLContext + import ssl context["foo"] = "FOO" # This causes an error when starting lazy listener executions - context["ssl_context"] = SSLContext() + context["ssl_context"] = ssl.create_default_context() await next_() # 2021-12-13 11:52:46 ERROR Failed to run a middleware function (error: cannot pickle 'SSLContext' object) From f11dbfbd06bf284a2ea4c65a4064f781c18fe5e0 Mon Sep 17 00:00:00 2001 From: Ale Mercado <104795114+srtaalej@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:41:24 -0400 Subject: [PATCH 205/282] fix(assistant): get_thread_context calls store.find() for user_message events (#1453) Co-authored-by: William Bergamin --- .../async_get_thread_context.py | 10 +++---- .../get_thread_context/get_thread_context.py | 10 +++---- tests/scenario_tests/test_events_assistant.py | 25 ++++++++++++++++++ .../test_events_assistant.py | 26 +++++++++++++++++++ 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/slack_bolt/context/get_thread_context/async_get_thread_context.py b/slack_bolt/context/get_thread_context/async_get_thread_context.py index cb8683a10..03f7c6076 100644 --- a/slack_bolt/context/get_thread_context/async_get_thread_context.py +++ b/slack_bolt/context/get_thread_context/async_get_thread_context.py @@ -31,14 +31,10 @@ async def __call__(self) -> Optional[AssistantThreadContext]: if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: diff --git a/slack_bolt/context/get_thread_context/get_thread_context.py b/slack_bolt/context/get_thread_context/get_thread_context.py index 0a77d2d9f..b9c9751e1 100644 --- a/slack_bolt/context/get_thread_context/get_thread_context.py +++ b/slack_bolt/context/get_thread_context/get_thread_context.py @@ -31,14 +31,10 @@ def __call__(self) -> Optional[AssistantThreadContext]: if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index a970c9fa4..a1c3f1343 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -133,6 +133,12 @@ def handle_bot_message(): app.assistant(assistant) + request = BoltRequest(body=user_message_event_body_with_action_token, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert listener_called.wait(timeout=0.1) is True + listener_called.clear() + request = BoltRequest(body=message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 @@ -332,6 +338,25 @@ def build_payload(event: dict) -> dict: } ) +user_message_event_body_with_action_token = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + "assistant_thread": {"action_token": "10647138185092.960436384805.afce3599"}, + } +) + message_changed_event_body = build_payload( { "type": "message", diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index 9b2e43eb1..9ccd80c11 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -157,6 +157,13 @@ async def handle_bot_message(): app.assistant(assistant) + request = AsyncBoltRequest(body=user_message_event_body_with_action_token, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await asyncio.sleep(0.1) + assert listener_called.is_set() + listener_called.clear() + request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 @@ -405,6 +412,25 @@ def build_payload(event: dict) -> dict: ) +user_message_event_body_with_action_token = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + "assistant_thread": {"action_token": "10647138185092.960436384805.afce3599"}, + } +) + message_changed_event_body = build_payload( { "type": "message", From 89088857d958c0ba34d036e90bc28db879c76ad5 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 26 Mar 2026 13:36:54 -0700 Subject: [PATCH 206/282] chore: improve type checking behavior (#1470) --- .github/workflows/ci-build.yml | 15 +++++++++++++-- slack_bolt/app/async_server.py | 11 +++++++---- slack_bolt/context/async_context.py | 7 +++++-- slack_bolt/context/context.py | 7 +++++-- .../listener/async_listener_error_handler.py | 7 ++++--- slack_bolt/listener/listener_error_handler.py | 7 ++++--- .../middleware/async_middleware_error_handler.py | 7 ++++--- slack_bolt/middleware/middleware_error_handler.py | 7 ++++--- slack_bolt/oauth/async_callback_options.py | 9 ++++++--- slack_bolt/oauth/callback_options.py | 9 ++++++--- 10 files changed, 58 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 6555a6531..6c4cd5a6a 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -44,8 +44,19 @@ jobs: uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - - name: Run mypy verification - run: ./scripts/run_mypy.sh + - name: Install synchronous dependencies + run: | + pip install -U pip + pip install -U . + pip install -r requirements/tools.txt + - name: Type check synchronous modules + run: mypy --config-file pyproject.toml --exclude "async_|/adapter/" + - name: Install async and adapter dependencies + run: | + pip install -r requirements/async.txt + pip install -r requirements/adapter.txt + - name: Type check all modules + run: mypy --config-file pyproject.toml unittest: name: Unit tests diff --git a/slack_bolt/app/async_server.py b/slack_bolt/app/async_server.py index 998cd5a4b..f21d35932 100644 --- a/slack_bolt/app/async_server.py +++ b/slack_bolt/app/async_server.py @@ -1,5 +1,5 @@ import logging -from typing import Optional +from typing import Optional, TYPE_CHECKING from aiohttp import web @@ -7,19 +7,22 @@ from slack_bolt.response import BoltResponse from slack_bolt.util.utils import get_boot_message +if TYPE_CHECKING: + from slack_bolt.app.async_app import AsyncApp + class AsyncSlackAppServer: port: int path: str host: str - bolt_app: "AsyncApp" # type: ignore[name-defined] + bolt_app: "AsyncApp" web_app: web.Application def __init__( self, port: int, path: str, - app: "AsyncApp", # type: ignore[name-defined] + app: "AsyncApp", host: Optional[str] = None, ): """Standalone AIOHTTP Web Server. @@ -34,7 +37,7 @@ def __init__( self.port = port self.path = path self.host = host if host is not None else "0.0.0.0" - self.bolt_app: "AsyncApp" = app # type: ignore[name-defined] + self.bolt_app: "AsyncApp" = app self.web_app = web.Application() self._bolt_oauth_flow = self.bolt_app.oauth_flow if self._bolt_oauth_flow: diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index 33f260d38..94b2b5cbe 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from slack_sdk.web.async_client import AsyncWebClient @@ -16,6 +16,9 @@ from slack_bolt.context.set_title.async_set_title import AsyncSetTitle from slack_bolt.util.utils import create_copy +if TYPE_CHECKING: + from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner + class AsyncBoltContext(BaseContext): """Context object associated with a request from Slack.""" @@ -42,7 +45,7 @@ def to_copyable(self) -> "AsyncBoltContext": # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "AsyncioListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index 6184d5083..b101460a5 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from slack_sdk import WebClient @@ -16,6 +16,9 @@ from slack_bolt.context.set_title import SetTitle from slack_bolt.util.utils import create_copy +if TYPE_CHECKING: + from slack_bolt.listener.thread_runner import ThreadListenerRunner + class BoltContext(BaseContext): """Context object associated with a request from Slack.""" @@ -43,7 +46,7 @@ def to_copyable(self) -> "BoltContext": # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] diff --git a/slack_bolt/listener/async_listener_error_handler.py b/slack_bolt/listener/async_listener_error_handler.py index 88f4b3510..b1a73458e 100644 --- a/slack_bolt/listener/async_listener_error_handler.py +++ b/slack_bolt/listener/async_listener_error_handler.py @@ -48,9 +48,10 @@ async def handle( ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class AsyncDefaultListenerErrorHandler(AsyncListenerErrorHandler): diff --git a/slack_bolt/listener/listener_error_handler.py b/slack_bolt/listener/listener_error_handler.py index 0ad98f738..7dd6d066b 100644 --- a/slack_bolt/listener/listener_error_handler.py +++ b/slack_bolt/listener/listener_error_handler.py @@ -48,9 +48,10 @@ def handle( ) returned_response = self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class DefaultListenerErrorHandler(ListenerErrorHandler): diff --git a/slack_bolt/middleware/async_middleware_error_handler.py b/slack_bolt/middleware/async_middleware_error_handler.py index 1957d3ab6..932b0770b 100644 --- a/slack_bolt/middleware/async_middleware_error_handler.py +++ b/slack_bolt/middleware/async_middleware_error_handler.py @@ -48,9 +48,10 @@ async def handle( ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class AsyncDefaultMiddlewareErrorHandler(AsyncMiddlewareErrorHandler): diff --git a/slack_bolt/middleware/middleware_error_handler.py b/slack_bolt/middleware/middleware_error_handler.py index fe57e400c..5919414bb 100644 --- a/slack_bolt/middleware/middleware_error_handler.py +++ b/slack_bolt/middleware/middleware_error_handler.py @@ -48,9 +48,10 @@ def handle( ) returned_response = self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class DefaultMiddlewareErrorHandler(MiddlewareErrorHandler): diff --git a/slack_bolt/oauth/async_callback_options.py b/slack_bolt/oauth/async_callback_options.py index 88518d7e8..e1c2b2e4c 100644 --- a/slack_bolt/oauth/async_callback_options.py +++ b/slack_bolt/oauth/async_callback_options.py @@ -1,6 +1,6 @@ import logging from logging import Logger -from typing import Optional, Callable, Awaitable +from typing import Optional, Callable, Awaitable, TYPE_CHECKING from slack_sdk.oauth import RedirectUriPageRenderer, OAuthStateUtils from slack_sdk.oauth.installation_store import Installation @@ -9,6 +9,9 @@ from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse +if TYPE_CHECKING: + from slack_bolt.oauth.async_oauth_settings import AsyncOAuthSettings + class AsyncSuccessArgs: def __init__( @@ -16,7 +19,7 @@ def __init__( *, request: AsyncBoltRequest, installation: Installation, - settings: "AsyncOAuthSettings", # type: ignore[name-defined] + settings: "AsyncOAuthSettings", default: "AsyncCallbackOptions", ): """The arguments for a success function. @@ -41,7 +44,7 @@ def __init__( reason: str, error: Optional[Exception] = None, suggested_status_code: int, - settings: "AsyncOAuthSettings", # type: ignore[name-defined] + settings: "AsyncOAuthSettings", default: "AsyncCallbackOptions", ): """The arguments for a failure function. diff --git a/slack_bolt/oauth/callback_options.py b/slack_bolt/oauth/callback_options.py index f267ed154..09584a365 100644 --- a/slack_bolt/oauth/callback_options.py +++ b/slack_bolt/oauth/callback_options.py @@ -1,6 +1,6 @@ import logging from logging import Logger -from typing import Optional, Callable +from typing import Optional, Callable, TYPE_CHECKING from slack_sdk.oauth import RedirectUriPageRenderer, OAuthStateUtils from slack_sdk.oauth.installation_store import Installation @@ -9,6 +9,9 @@ from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse +if TYPE_CHECKING: + from slack_bolt.oauth.oauth_settings import OAuthSettings + class SuccessArgs: def __init__( @@ -16,7 +19,7 @@ def __init__( *, request: BoltRequest, installation: Installation, - settings: "OAuthSettings", # type: ignore[name-defined] + settings: "OAuthSettings", default: "CallbackOptions", ): """The arguments for a success function. @@ -41,7 +44,7 @@ def __init__( reason: str, error: Optional[Exception] = None, suggested_status_code: int, - settings: "OAuthSettings", # type: ignore[name-defined] + settings: "OAuthSettings", default: "CallbackOptions", ): """The arguments for a failure function. From 9d0e0af36109393456b9663e539d9fa642d5711b Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 1 Apr 2026 14:55:30 -0400 Subject: [PATCH 207/282] refactor: rename AttachingAgentKwargs middleware to AttachingConversationKwargs (#1473) --- slack_bolt/app/app.py | 14 +++++++------- slack_bolt/app/async_app.py | 14 +++++++------- slack_bolt/kwargs_injection/args.py | 2 +- slack_bolt/middleware/__init__.py | 4 ++-- slack_bolt/middleware/assistant/assistant.py | 4 ++-- slack_bolt/middleware/assistant/async_assistant.py | 6 ++++-- slack_bolt/middleware/async_builtins.py | 4 ++-- .../middleware/attaching_agent_kwargs/__init__.py | 5 ----- .../attaching_conversation_kwargs/__init__.py | 5 +++++ .../async_attaching_conversation_kwargs.py} | 2 +- .../attaching_conversation_kwargs.py} | 2 +- .../test_events_assistant_without_middleware.py | 4 ++-- .../scenario_tests_async/test_events_assistant.py | 2 +- .../test_events_assistant_without_middleware.py | 4 ++-- .../__init__.py | 0 .../test_attaching_conversation_kwargs.py} | 12 ++++++------ .../__init__.py | 0 .../test_async_attaching_conversation_kwargs.py} | 14 ++++++++------ 18 files changed, 51 insertions(+), 47 deletions(-) delete mode 100644 slack_bolt/middleware/attaching_agent_kwargs/__init__.py create mode 100644 slack_bolt/middleware/attaching_conversation_kwargs/__init__.py rename slack_bolt/middleware/{attaching_agent_kwargs/async_attaching_agent_kwargs.py => attaching_conversation_kwargs/async_attaching_conversation_kwargs.py} (97%) rename slack_bolt/middleware/{attaching_agent_kwargs/attaching_agent_kwargs.py => attaching_conversation_kwargs/attaching_conversation_kwargs.py} (98%) rename tests/slack_bolt/middleware/{attaching_agent_kwargs => attaching_conversation_kwargs}/__init__.py (100%) rename tests/slack_bolt/middleware/{attaching_agent_kwargs/test_attaching_agent_kwargs.py => attaching_conversation_kwargs/test_attaching_conversation_kwargs.py} (88%) rename tests/slack_bolt_async/middleware/{attaching_agent_kwargs => attaching_conversation_kwargs}/__init__.py (100%) rename tests/slack_bolt_async/middleware/{attaching_agent_kwargs/test_async_attaching_agent_kwargs.py => attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py} (87%) diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index 566eb82d7..0af27913c 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -69,7 +69,7 @@ IgnoringSelfEvents, CustomMiddleware, AttachingFunctionToken, - AttachingAgentKwargs, + AttachingConversationKwargs, ) from slack_bolt.middleware.assistant import Assistant from slack_bolt.middleware.message_listener_matches import MessageListenerMatches @@ -133,7 +133,7 @@ def __init__( listener_executor: Optional[Executor] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, - attaching_agent_kwargs_enabled: bool = True, + attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -354,7 +354,7 @@ def message_hello(message, say): listener_executor = ThreadPoolExecutor(max_workers=5) self._assistant_thread_context_store = assistant_thread_context_store - self._attaching_agent_kwargs_enabled = attaching_agent_kwargs_enabled + self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( @@ -844,8 +844,8 @@ def ask_for_introduction(event, say): def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) - if self._attaching_agent_kwargs_enabled: - middleware.insert(0, AttachingAgentKwargs(self._assistant_thread_context_store)) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -903,8 +903,8 @@ def __call__(*args, **kwargs): primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) - if self._attaching_agent_kwargs_enabled: - middleware.insert(0, AttachingAgentKwargs(self._assistant_thread_context_store)) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index 9cd8c911f..cc94f9e15 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -86,7 +86,7 @@ AsyncIgnoringSelfEvents, AsyncUrlVerification, AsyncAttachingFunctionToken, - AsyncAttachingAgentKwargs, + AsyncAttachingConversationKwargs, ) from slack_bolt.middleware.async_custom_middleware import ( AsyncMiddleware, @@ -142,7 +142,7 @@ def __init__( verification_token: Optional[str] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, - attaching_agent_kwargs_enabled: bool = True, + attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -363,7 +363,7 @@ async def message_hello(message, say): # async function self._async_listeners: List[AsyncListener] = [] self._assistant_thread_context_store = assistant_thread_context_store - self._attaching_agent_kwargs_enabled = attaching_agent_kwargs_enabled + self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._async_listener_runner = AsyncioListenerRunner( @@ -872,8 +872,8 @@ async def ask_for_introduction(event, say): def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, True, base_logger=self._base_logger) - if self._attaching_agent_kwargs_enabled: - middleware.insert(0, AsyncAttachingAgentKwargs(self._assistant_thread_context_store)) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -934,8 +934,8 @@ def __call__(*args, **kwargs): asyncio=True, base_logger=self._base_logger, ) - if self._attaching_agent_kwargs_enabled: - middleware.insert(0, AsyncAttachingAgentKwargs(self._assistant_thread_context_store)) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, AsyncMessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) diff --git a/slack_bolt/kwargs_injection/args.py b/slack_bolt/kwargs_injection/args.py index 4cd70176d..f2b4099d6 100644 --- a/slack_bolt/kwargs_injection/args.py +++ b/slack_bolt/kwargs_injection/args.py @@ -104,7 +104,7 @@ def handle_buttons(args): save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" say_stream: Optional[SayStream] - """`say_stream()` utility function for AI Agents & Assistants""" + """`say_stream()` utility function for conversations, AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" diff --git a/slack_bolt/middleware/__init__.py b/slack_bolt/middleware/__init__.py index 7b51fb239..c28ffd78d 100644 --- a/slack_bolt/middleware/__init__.py +++ b/slack_bolt/middleware/__init__.py @@ -17,7 +17,7 @@ from .ssl_check import SslCheck from .url_verification import UrlVerification from .attaching_function_token import AttachingFunctionToken -from .attaching_agent_kwargs import AttachingAgentKwargs +from .attaching_conversation_kwargs import AttachingConversationKwargs builtin_middleware_classes = [ SslCheck, @@ -42,6 +42,6 @@ "SslCheck", "UrlVerification", "AttachingFunctionToken", - "AttachingAgentKwargs", + "AttachingConversationKwargs", "builtin_middleware_classes", ] diff --git a/slack_bolt/middleware/assistant/assistant.py b/slack_bolt/middleware/assistant/assistant.py index 9696e826e..ad842f94d 100644 --- a/slack_bolt/middleware/assistant/assistant.py +++ b/slack_bolt/middleware/assistant/assistant.py @@ -7,7 +7,7 @@ from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore from slack_bolt.listener_matcher.builtins import build_listener_matcher -from slack_bolt.middleware.attaching_agent_kwargs import AttachingAgentKwargs +from slack_bolt.middleware.attaching_conversation_kwargs import AttachingConversationKwargs from slack_bolt.request.request import BoltRequest from slack_bolt.response.response import BoltResponse from slack_bolt.listener_matcher import CustomListenerMatcher @@ -272,7 +272,7 @@ def build_listener( return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] - middleware.insert(0, AttachingAgentKwargs(self.thread_context_store)) + middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) diff --git a/slack_bolt/middleware/assistant/async_assistant.py b/slack_bolt/middleware/assistant/async_assistant.py index d841e2de0..588de8b41 100644 --- a/slack_bolt/middleware/assistant/async_assistant.py +++ b/slack_bolt/middleware/assistant/async_assistant.py @@ -8,7 +8,9 @@ from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner from slack_bolt.listener_matcher.builtins import build_listener_matcher -from slack_bolt.middleware.attaching_agent_kwargs.async_attaching_agent_kwargs import AsyncAttachingAgentKwargs +from slack_bolt.middleware.attaching_conversation_kwargs.async_attaching_conversation_kwargs import ( + AsyncAttachingConversationKwargs, +) from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse from slack_bolt.error import BoltError @@ -301,7 +303,7 @@ def build_listener( return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] - middleware.insert(0, AsyncAttachingAgentKwargs(self.thread_context_store)) + middleware.insert(0, AsyncAttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) diff --git a/slack_bolt/middleware/async_builtins.py b/slack_bolt/middleware/async_builtins.py index 755b55c20..8de07fb88 100644 --- a/slack_bolt/middleware/async_builtins.py +++ b/slack_bolt/middleware/async_builtins.py @@ -10,7 +10,7 @@ AsyncMessageListenerMatches, ) from .attaching_function_token.async_attaching_function_token import AsyncAttachingFunctionToken -from .attaching_agent_kwargs.async_attaching_agent_kwargs import AsyncAttachingAgentKwargs +from .attaching_conversation_kwargs.async_attaching_conversation_kwargs import AsyncAttachingConversationKwargs __all__ = [ "AsyncIgnoringSelfEvents", @@ -19,5 +19,5 @@ "AsyncUrlVerification", "AsyncMessageListenerMatches", "AsyncAttachingFunctionToken", - "AsyncAttachingAgentKwargs", + "AsyncAttachingConversationKwargs", ] diff --git a/slack_bolt/middleware/attaching_agent_kwargs/__init__.py b/slack_bolt/middleware/attaching_agent_kwargs/__init__.py deleted file mode 100644 index 98926fc14..000000000 --- a/slack_bolt/middleware/attaching_agent_kwargs/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .attaching_agent_kwargs import AttachingAgentKwargs - -__all__ = [ - "AttachingAgentKwargs", -] diff --git a/slack_bolt/middleware/attaching_conversation_kwargs/__init__.py b/slack_bolt/middleware/attaching_conversation_kwargs/__init__.py new file mode 100644 index 000000000..ec72e0037 --- /dev/null +++ b/slack_bolt/middleware/attaching_conversation_kwargs/__init__.py @@ -0,0 +1,5 @@ +from .attaching_conversation_kwargs import AttachingConversationKwargs + +__all__ = [ + "AttachingConversationKwargs", +] diff --git a/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.py similarity index 97% rename from slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py rename to slack_bolt/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.py index 82f1a7671..315ec2a50 100644 --- a/slack_bolt/middleware/attaching_agent_kwargs/async_attaching_agent_kwargs.py +++ b/slack_bolt/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.py @@ -10,7 +10,7 @@ from slack_bolt.response import BoltResponse -class AsyncAttachingAgentKwargs(AsyncMiddleware): +class AsyncAttachingConversationKwargs(AsyncMiddleware): thread_context_store: Optional[AsyncAssistantThreadContextStore] diff --git a/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py b/slack_bolt/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.py similarity index 98% rename from slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py rename to slack_bolt/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.py index 70f41d561..33847fd56 100644 --- a/slack_bolt/middleware/attaching_agent_kwargs/attaching_agent_kwargs.py +++ b/slack_bolt/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.py @@ -10,7 +10,7 @@ from slack_bolt.response.response import BoltResponse -class AttachingAgentKwargs(Middleware): +class AttachingConversationKwargs(Middleware): thread_context_store: Optional[AssistantThreadContextStore] diff --git a/tests/scenario_tests/test_events_assistant_without_middleware.py b/tests/scenario_tests/test_events_assistant_without_middleware.py index c95f16f99..18072c05e 100644 --- a/tests/scenario_tests/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests/test_events_assistant_without_middleware.py @@ -245,8 +245,8 @@ def handle_message_event( assert response.status == 200 assert listener_called.wait(timeout=0.1) is True - def test_assistant_events_agent_kwargs_disabled(self): - app = App(client=self.web_client, attaching_agent_kwargs_enabled=False) + def test_assistant_events_conversation_kwargs_disabled(self): + app = App(client=self.web_client, attaching_conversation_kwargs_enabled=False) listener_called = Event() diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index 9ccd80c11..edc77ecf3 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -216,7 +216,7 @@ async def handle_bot_message(): @pytest.mark.asyncio async def test_assistant_events_kwargs_disabled(self): - app = AsyncApp(client=self.web_client, attaching_agent_kwargs_enabled=False) + app = AsyncApp(client=self.web_client, attaching_conversation_kwargs_enabled=False) listener_called = asyncio.Event() @app.event("assistant_thread_started") diff --git a/tests/scenario_tests_async/test_events_assistant_without_middleware.py b/tests/scenario_tests_async/test_events_assistant_without_middleware.py index 4e82cb2c1..d72b09b04 100644 --- a/tests/scenario_tests_async/test_events_assistant_without_middleware.py +++ b/tests/scenario_tests_async/test_events_assistant_without_middleware.py @@ -268,8 +268,8 @@ async def handle_message_event( assert (await asyncio.wait_for(listener_called.wait(), timeout=0.1)) is True @pytest.mark.asyncio - async def test_assistant_events_agent_kwargs_disabled(self): - app = AsyncApp(client=self.web_client, attaching_agent_kwargs_enabled=False) + async def test_assistant_events_conversation_kwargs_disabled(self): + app = AsyncApp(client=self.web_client, attaching_conversation_kwargs_enabled=False) listener_called = asyncio.Event() diff --git a/tests/slack_bolt/middleware/attaching_agent_kwargs/__init__.py b/tests/slack_bolt/middleware/attaching_conversation_kwargs/__init__.py similarity index 100% rename from tests/slack_bolt/middleware/attaching_agent_kwargs/__init__.py rename to tests/slack_bolt/middleware/attaching_conversation_kwargs/__init__.py diff --git a/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py b/tests/slack_bolt/middleware/attaching_conversation_kwargs/test_attaching_conversation_kwargs.py similarity index 88% rename from tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py rename to tests/slack_bolt/middleware/attaching_conversation_kwargs/test_attaching_conversation_kwargs.py index 8e626fd0c..b7785eb50 100644 --- a/tests/slack_bolt/middleware/attaching_agent_kwargs/test_attaching_agent_kwargs.py +++ b/tests/slack_bolt/middleware/attaching_conversation_kwargs/test_attaching_conversation_kwargs.py @@ -1,6 +1,6 @@ from slack_sdk import WebClient -from slack_bolt.middleware.attaching_agent_kwargs import AttachingAgentKwargs +from slack_bolt.middleware.attaching_conversation_kwargs import AttachingConversationKwargs from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse from tests.scenario_tests.test_events_assistant import ( @@ -17,9 +17,9 @@ def next(): ASSISTANT_KWARGS = ("say", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") -class TestAttachingAgentKwargs: +class TestAttachingConversationKwargs: def test_assistant_event_attaches_kwargs(self): - middleware = AttachingAgentKwargs() + middleware = AttachingConversationKwargs() req = BoltRequest(body=thread_started_event_body, mode="socket_mode") req.context["client"] = WebClient(token="xoxb-test") @@ -33,7 +33,7 @@ def test_assistant_event_attaches_kwargs(self): assert "set_status" in req.context def test_user_message_event_attaches_kwargs(self): - middleware = AttachingAgentKwargs() + middleware = AttachingConversationKwargs() req = BoltRequest(body=user_message_event_body, mode="socket_mode") req.context["client"] = WebClient(token="xoxb-test") @@ -47,7 +47,7 @@ def test_user_message_event_attaches_kwargs(self): assert "set_status" in req.context def test_non_assistant_event_does_not_attach_kwargs(self): - middleware = AttachingAgentKwargs() + middleware = AttachingConversationKwargs() req = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") req.context["client"] = WebClient(token="xoxb-test") @@ -60,7 +60,7 @@ def test_non_assistant_event_does_not_attach_kwargs(self): assert "set_status" in req.context def test_non_event_does_not_attach_kwargs(self): - middleware = AttachingAgentKwargs() + middleware = AttachingConversationKwargs() req = BoltRequest(body="payload={}", headers={}) resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) diff --git a/tests/slack_bolt_async/middleware/attaching_agent_kwargs/__init__.py b/tests/slack_bolt_async/middleware/attaching_conversation_kwargs/__init__.py similarity index 100% rename from tests/slack_bolt_async/middleware/attaching_agent_kwargs/__init__.py rename to tests/slack_bolt_async/middleware/attaching_conversation_kwargs/__init__.py diff --git a/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py b/tests/slack_bolt_async/middleware/attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py similarity index 87% rename from tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py rename to tests/slack_bolt_async/middleware/attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py index 61aa0b59e..a00b35cd3 100644 --- a/tests/slack_bolt_async/middleware/attaching_agent_kwargs/test_async_attaching_agent_kwargs.py +++ b/tests/slack_bolt_async/middleware/attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py @@ -1,7 +1,9 @@ import pytest from slack_sdk.web.async_client import AsyncWebClient -from slack_bolt.middleware.attaching_agent_kwargs.async_attaching_agent_kwargs import AsyncAttachingAgentKwargs +from slack_bolt.middleware.attaching_conversation_kwargs.async_attaching_conversation_kwargs import ( + AsyncAttachingConversationKwargs, +) from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse from tests.scenario_tests_async.test_events_assistant import ( @@ -18,10 +20,10 @@ async def next(): ASSISTANT_KWARGS = ("say", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") -class TestAsyncAttachingAgentKwargs: +class TestAsyncAttachingConversationKwargs: @pytest.mark.asyncio async def test_assistant_event_attaches_kwargs(self): - middleware = AsyncAttachingAgentKwargs() + middleware = AsyncAttachingConversationKwargs() req = AsyncBoltRequest(body=thread_started_event_body, mode="socket_mode") req.context["client"] = AsyncWebClient(token="xoxb-test") @@ -36,7 +38,7 @@ async def test_assistant_event_attaches_kwargs(self): @pytest.mark.asyncio async def test_user_message_event_attaches_kwargs(self): - middleware = AsyncAttachingAgentKwargs() + middleware = AsyncAttachingConversationKwargs() req = AsyncBoltRequest(body=user_message_event_body, mode="socket_mode") req.context["client"] = AsyncWebClient(token="xoxb-test") @@ -51,7 +53,7 @@ async def test_user_message_event_attaches_kwargs(self): @pytest.mark.asyncio async def test_non_assistant_event_does_not_attach_kwargs(self): - middleware = AsyncAttachingAgentKwargs() + middleware = AsyncAttachingConversationKwargs() req = AsyncBoltRequest(body=channel_user_message_event_body, mode="socket_mode") req.context["client"] = AsyncWebClient(token="xoxb-test") @@ -65,7 +67,7 @@ async def test_non_assistant_event_does_not_attach_kwargs(self): @pytest.mark.asyncio async def test_non_event_does_not_attach_kwargs(self): - middleware = AsyncAttachingAgentKwargs() + middleware = AsyncAttachingConversationKwargs() req = AsyncBoltRequest(body="payload={}", headers={}) resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) From 4dee16d96ca41a62af417e3345e2fa4e2d813a99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 02:43:41 +0000 Subject: [PATCH 208/282] chore(deps): bump actions/download-artifact from 8.0.0 to 8.0.1 (#1474) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 7ec974574..dfc224c83 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -52,7 +52,7 @@ jobs: steps: - name: Retrieve dist folder - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: release-dist path: dist/ @@ -76,7 +76,7 @@ jobs: steps: - name: Retrieve dist folder - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: release-dist path: dist/ From 3f9d3761dba44a1c259055d56a046ba8e462a8c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:34:37 -0400 Subject: [PATCH 209/282] chore(deps): bump codecov/codecov-action from 5.5.2 to 6.0.0 (#1475) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 6c4cd5a6a..324ff7d80 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -126,7 +126,7 @@ jobs: pytest tests/scenario_tests_async/ --junitxml=reports/test_scenario_async.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 with: directory: ./reports/ fail_ci_if_error: true @@ -162,7 +162,7 @@ jobs: run: | pytest --cov=./slack_bolt/ --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 with: fail_ci_if_error: true report_type: coverage From 13a6dff9d6682593982604e587b7340dcc2e9d60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:39:18 -0400 Subject: [PATCH 210/282] chore(deps): bump slackapi/slack-github-action from 2.1.1 to 3.0.1 (#1476) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 324ff7d80..6d504ea83 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -179,7 +179,7 @@ jobs: if: ${{ !success() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' }} steps: - name: Send notifications of failing tests - uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1 + uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1 with: errors: true webhook: ${{ secrets.SLACK_REGRESSION_FAILURES_WEBHOOK_URL }} From dbe1590498a80903b5b5ce559b89c4640e84c775 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:46:04 -0400 Subject: [PATCH 211/282] chore(deps): bump dependabot/fetch-metadata from 2.5.0 to 3.0.0 (#1477) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependencies.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 824d57701..9666057aa 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Collect metadata id: metadata - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 + uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v3.0.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Approve From 064ef2e83ad9035827e1267243ee56130e5b12fd Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 6 Apr 2026 17:36:31 -0400 Subject: [PATCH 212/282] chore: remove experiment around say_stream (#1471) Co-authored-by: Eden Zimbelman --- .../context/say_stream/async_say_stream.py | 14 +------------ slack_bolt/context/say_stream/say_stream.py | 14 +------------ slack_bolt/warning/__init__.py | 7 ------- tests/slack_bolt/context/test_say_stream.py | 20 ++++-------------- .../context/test_async_say_stream.py | 21 ++++--------------- 5 files changed, 10 insertions(+), 66 deletions(-) delete mode 100644 slack_bolt/warning/__init__.py diff --git a/slack_bolt/context/say_stream/async_say_stream.py b/slack_bolt/context/say_stream/async_say_stream.py index dc752d02a..af776891b 100644 --- a/slack_bolt/context/say_stream/async_say_stream.py +++ b/slack_bolt/context/say_stream/async_say_stream.py @@ -1,11 +1,8 @@ -import warnings from typing import Optional from slack_sdk.web.async_client import AsyncWebClient from slack_sdk.web.async_chat_stream import AsyncChatStream -from slack_bolt.warning import ExperimentalWarning - class AsyncSayStream: client: AsyncWebClient @@ -39,16 +36,7 @@ async def __call__( thread_ts: Optional[str] = None, **kwargs, ) -> AsyncChatStream: - """Starts a new chat stream with context. - - Warning: This is an experimental feature and may change in future versions. - """ - warnings.warn( - "say_stream is experimental and may change in future versions.", - category=ExperimentalWarning, - stacklevel=2, - ) - + """Starts a new chat stream with context.""" channel = channel or self.channel thread_ts = thread_ts or self.thread_ts if channel is None: diff --git a/slack_bolt/context/say_stream/say_stream.py b/slack_bolt/context/say_stream/say_stream.py index 1e1d7985f..b6a5ca797 100644 --- a/slack_bolt/context/say_stream/say_stream.py +++ b/slack_bolt/context/say_stream/say_stream.py @@ -1,11 +1,8 @@ -import warnings from typing import Optional from slack_sdk import WebClient from slack_sdk.web.chat_stream import ChatStream -from slack_bolt.warning import ExperimentalWarning - class SayStream: client: WebClient @@ -39,16 +36,7 @@ def __call__( thread_ts: Optional[str] = None, **kwargs, ) -> ChatStream: - """Starts a new chat stream with context. - - Warning: This is an experimental feature and may change in future versions. - """ - warnings.warn( - "say_stream is experimental and may change in future versions.", - category=ExperimentalWarning, - stacklevel=2, - ) - + """Starts a new chat stream with context.""" channel = channel or self.channel thread_ts = thread_ts or self.thread_ts if channel is None: diff --git a/slack_bolt/warning/__init__.py b/slack_bolt/warning/__init__.py deleted file mode 100644 index 4991f4cd9..000000000 --- a/slack_bolt/warning/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Bolt specific warning types.""" - - -class ExperimentalWarning(FutureWarning): - """Warning for features that are still in experimental phase.""" - - pass diff --git a/tests/slack_bolt/context/test_say_stream.py b/tests/slack_bolt/context/test_say_stream.py index c8f4c3a31..29d244a65 100644 --- a/tests/slack_bolt/context/test_say_stream.py +++ b/tests/slack_bolt/context/test_say_stream.py @@ -2,7 +2,6 @@ from slack_sdk import WebClient from slack_bolt.context.say_stream.say_stream import SayStream -from slack_bolt.warning import ExperimentalWarning from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server @@ -20,15 +19,13 @@ def teardown_method(self): def test_missing_channel_raises(self): say_stream = SayStream(client=self.web_client, channel=None, thread_ts="111.222") - with pytest.warns(ExperimentalWarning): - with pytest.raises(ValueError, match="channel"): - say_stream() + with pytest.raises(ValueError, match="channel"): + say_stream() def test_missing_thread_ts_raises(self): say_stream = SayStream(client=self.web_client, channel="C111", thread_ts=None) - with pytest.warns(ExperimentalWarning): - with pytest.raises(ValueError, match="thread_ts"): - say_stream() + with pytest.raises(ValueError, match="thread_ts"): + say_stream() def test_default_params(self): say_stream = SayStream( @@ -92,12 +89,3 @@ def test_buffer_size_overrides(self): "recipient_user_id": "U222", "task_display_mode": None, } - - def test_experimental_warning(self): - say_stream = SayStream( - client=self.web_client, - channel="C111", - thread_ts="111.222", - ) - with pytest.warns(ExperimentalWarning, match="say_stream is experimental"): - say_stream() diff --git a/tests/slack_bolt_async/context/test_async_say_stream.py b/tests/slack_bolt_async/context/test_async_say_stream.py index fbc4c5c7e..016549bd6 100644 --- a/tests/slack_bolt_async/context/test_async_say_stream.py +++ b/tests/slack_bolt_async/context/test_async_say_stream.py @@ -2,7 +2,6 @@ from slack_sdk.web.async_client import AsyncWebClient from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream -from slack_bolt.warning import ExperimentalWarning from tests.mock_web_api_server import ( cleanup_mock_web_api_server, setup_mock_web_api_server, @@ -29,16 +28,14 @@ def setup_teardown(self): @pytest.mark.asyncio async def test_missing_channel_raises(self): say_stream = AsyncSayStream(client=self.web_client, channel=None, thread_ts="111.222") - with pytest.warns(ExperimentalWarning): - with pytest.raises(ValueError, match="channel"): - await say_stream() + with pytest.raises(ValueError, match="channel"): + await say_stream() @pytest.mark.asyncio async def test_missing_thread_ts_raises(self): say_stream = AsyncSayStream(client=self.web_client, channel="C111", thread_ts=None) - with pytest.warns(ExperimentalWarning): - with pytest.raises(ValueError, match="thread_ts"): - await say_stream() + with pytest.raises(ValueError, match="thread_ts"): + await say_stream() @pytest.mark.asyncio async def test_default_params(self): @@ -105,13 +102,3 @@ async def test_buffer_size_overrides(self): "recipient_user_id": "U222", "task_display_mode": None, } - - @pytest.mark.asyncio - async def test_experimental_warning(self): - say_stream = AsyncSayStream( - client=self.web_client, - channel="C111", - thread_ts="111.222", - ) - with pytest.warns(ExperimentalWarning, match="say_stream is experimental"): - await say_stream() From c64d69d2b64801602c849aa56e0ba2d4161e1f98 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 6 Apr 2026 15:49:13 -0700 Subject: [PATCH 213/282] chore(release): version 1.28.0 (#1480) --- docs/reference/app/app.html | 30 ++- docs/reference/app/async_app.html | 34 ++- docs/reference/app/async_server.html | 6 +- docs/reference/app/index.html | 30 ++- docs/reference/async_app.html | 229 ++++++++++++++---- .../authorization/authorize_result.html | 4 +- docs/reference/authorization/index.html | 4 +- .../assistant/assistant_utilities.html | 14 ++ .../assistant/async_assistant_utilities.html | 14 ++ .../thread_context_store/file/index.html | 2 +- docs/reference/context/async_context.html | 25 +- docs/reference/context/base_context.html | 1 + docs/reference/context/context.html | 25 +- .../async_get_thread_context.html | 10 +- .../get_thread_context.html | 10 +- .../context/get_thread_context/index.html | 10 +- docs/reference/context/index.html | 30 ++- .../context/say_stream/async_say_stream.html | 174 +++++++++++++ docs/reference/context/say_stream/index.html | 191 +++++++++++++++ .../context/say_stream/say_stream.html | 174 +++++++++++++ docs/reference/error/index.html | 2 +- docs/reference/index.html | 212 +++++++++++++--- docs/reference/kwargs_injection/args.html | 11 +- .../kwargs_injection/async_args.html | 11 +- .../kwargs_injection/async_utils.html | 5 +- docs/reference/kwargs_injection/index.html | 16 +- docs/reference/kwargs_injection/utils.html | 5 +- .../async_listener_error_handler.html | 7 +- .../listener/listener_error_handler.html | 7 +- docs/reference/logger/messages.html | 4 +- .../middleware/assistant/assistant.html | 43 ++-- .../middleware/assistant/async_assistant.html | 59 +++-- .../reference/middleware/assistant/index.html | 43 ++-- docs/reference/middleware/async_builtins.html | 82 +++++++ .../middleware/async_middleware.html | 1 + .../async_middleware_error_handler.html | 7 +- .../async_attaching_conversation_kwargs.html | 155 ++++++++++++ .../attaching_conversation_kwargs.html | 149 ++++++++++++ .../attaching_conversation_kwargs/index.html | 166 +++++++++++++ docs/reference/middleware/index.html | 82 +++++++ docs/reference/middleware/middleware.html | 1 + .../middleware/middleware_error_handler.html | 7 +- .../oauth/async_callback_options.html | 4 +- .../reference/oauth/async_oauth_settings.html | 2 +- docs/reference/oauth/callback_options.html | 4 +- docs/reference/oauth/oauth_settings.html | 2 +- docs/reference/request/internals.html | 75 +++--- slack_bolt/version.py | 2 +- 48 files changed, 1868 insertions(+), 313 deletions(-) create mode 100644 docs/reference/context/say_stream/async_say_stream.html create mode 100644 docs/reference/context/say_stream/index.html create mode 100644 docs/reference/context/say_stream/say_stream.html create mode 100644 docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html create mode 100644 docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html create mode 100644 docs/reference/middleware/attaching_conversation_kwargs/index.html diff --git a/docs/reference/app/app.html b/docs/reference/app/app.html index c91d020ef..bf0d5ee00 100644 --- a/docs/reference/app/app.html +++ b/docs/reference/app/app.html @@ -48,7 +48,7 @@

    Classes

    class App -(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    token_verification_enabled: bool = True,
    client: slack_sdk.web.client.WebClient | None = None,
    before_authorize: Middleware | Callable[..., Any] | None = None,
    authorize: Callable[..., AuthorizeResult] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: OAuthSettings | None = None,
    oauth_flow: OAuthFlow | None = None,
    verification_token: str | None = None,
    listener_executor: concurrent.futures._base.Executor | None = None,
    assistant_thread_context_store: AssistantThreadContextStore | None = None)
    +(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    token_verification_enabled: bool = True,
    client: slack_sdk.web.client.WebClient | None = None,
    before_authorize: Middleware | Callable[..., Any] | None = None,
    authorize: Callable[..., AuthorizeResult] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: OAuthSettings | None = None,
    oauth_flow: OAuthFlow | None = None,
    verification_token: str | None = None,
    listener_executor: concurrent.futures._base.Executor | None = None,
    assistant_thread_context_store: AssistantThreadContextStore | None = None,
    attaching_conversation_kwargs_enabled: bool = True)
    @@ -95,6 +95,7 @@

    Classes

    listener_executor: Optional[Executor] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, + attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -315,6 +316,7 @@

    Classes

    listener_executor = ThreadPoolExecutor(max_workers=5) self._assistant_thread_context_store = assistant_thread_context_store + self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( @@ -799,10 +801,13 @@

    Classes

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -860,6 +865,8 @@

    Classes

    primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -1356,20 +1363,6 @@

    Classes

    # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner - # For AI Agents & Assistants - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=to_event(req.body), # type:ignore[arg-type] - context=req.context, - thread_context_store=self._assistant_thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - @staticmethod def _to_listener_functions( kwargs: dict, @@ -1415,7 +1408,7 @@

    Classes

    CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, # type:ignore[arg-type] + lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, @@ -2203,10 +2196,13 @@

    Args

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -2410,6 +2406,8 @@

    Args

    primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) diff --git a/docs/reference/app/async_app.html b/docs/reference/app/async_app.html index 9cbc801d0..cf4c651cb 100644 --- a/docs/reference/app/async_app.html +++ b/docs/reference/app/async_app.html @@ -48,7 +48,7 @@

    Classes

    class AsyncApp -(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    client: slack_sdk.web.async_client.AsyncWebClient | None = None,
    before_authorize: AsyncMiddleware | Callable[..., Awaitable[Any]] | None = None,
    authorize: Callable[..., Awaitable[AuthorizeResult]] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: AsyncOAuthSettings | None = None,
    oauth_flow: AsyncOAuthFlow | None = None,
    verification_token: str | None = None,
    assistant_thread_context_store: AsyncAssistantThreadContextStore | None = None)
    +(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    client: slack_sdk.web.async_client.AsyncWebClient | None = None,
    before_authorize: AsyncMiddleware | Callable[..., Awaitable[Any]] | None = None,
    authorize: Callable[..., Awaitable[AuthorizeResult]] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: AsyncOAuthSettings | None = None,
    oauth_flow: AsyncOAuthFlow | None = None,
    verification_token: str | None = None,
    assistant_thread_context_store: AsyncAssistantThreadContextStore | None = None,
    attaching_conversation_kwargs_enabled: bool = True)
    @@ -92,6 +92,7 @@

    Classes

    verification_token: Optional[str] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, + attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -312,6 +313,7 @@

    Classes

    self._async_listeners: List[AsyncListener] = [] self._assistant_thread_context_store = assistant_thread_context_store + self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._async_listener_runner = AsyncioListenerRunner( @@ -565,7 +567,7 @@

    Classes

    self._framework_logger.debug(debug_checking_listener(listener_name)) if await listener.async_matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - (middleware_resp, next_was_not_called) = await listener.run_async_middleware( + middleware_resp, next_was_not_called = await listener.run_async_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: @@ -815,10 +817,13 @@

    Classes

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, True, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -879,6 +884,8 @@

    Classes

    asyncio=True, base_logger=self._base_logger, ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, AsyncMessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -1380,20 +1387,6 @@

    Classes

    # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner - # For AI Agents & Assistants - if is_assistant_event(req.body): - assistant = AsyncAssistantUtilities( - payload=to_event(req.body), # type:ignore[arg-type] - context=req.context, - thread_context_store=self._assistant_thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - @staticmethod def _to_listener_functions( kwargs: dict, @@ -1444,7 +1437,7 @@

    Classes

    AsyncCustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, # type:ignore[arg-type] + lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, @@ -1794,7 +1787,7 @@

    Args

    self._framework_logger.debug(debug_checking_listener(listener_name)) if await listener.async_matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - (middleware_resp, next_was_not_called) = await listener.run_async_middleware( + middleware_resp, next_was_not_called = await listener.run_async_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: @@ -2243,10 +2236,13 @@

    Args

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, True, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__
    @@ -2454,6 +2450,8 @@

    Args

    asyncio=True, base_logger=self._base_logger, ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, AsyncMessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) diff --git a/docs/reference/app/async_server.html b/docs/reference/app/async_server.html index b95b8a2b3..5eefe90dd 100644 --- a/docs/reference/app/async_server.html +++ b/docs/reference/app/async_server.html @@ -59,14 +59,14 @@

    Classes

    port: int path: str host: str - bolt_app: "AsyncApp" # type: ignore[name-defined] + bolt_app: "AsyncApp" web_app: web.Application def __init__( self, port: int, path: str, - app: "AsyncApp", # type: ignore[name-defined] + app: "AsyncApp", host: Optional[str] = None, ): """Standalone AIOHTTP Web Server. @@ -81,7 +81,7 @@

    Classes

    self.port = port self.path = path self.host = host if host is not None else "0.0.0.0" - self.bolt_app: "AsyncApp" = app # type: ignore[name-defined] + self.bolt_app: "AsyncApp" = app self.web_app = web.Application() self._bolt_oauth_flow = self.bolt_app.oauth_flow if self._bolt_oauth_flow: diff --git a/docs/reference/app/index.html b/docs/reference/app/index.html index 8821e5af9..32e006944 100644 --- a/docs/reference/app/index.html +++ b/docs/reference/app/index.html @@ -67,7 +67,7 @@

    Classes

    class App -(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    token_verification_enabled: bool = True,
    client: slack_sdk.web.client.WebClient | None = None,
    before_authorize: Middleware | Callable[..., Any] | None = None,
    authorize: Callable[..., AuthorizeResult] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: OAuthSettings | None = None,
    oauth_flow: OAuthFlow | None = None,
    verification_token: str | None = None,
    listener_executor: concurrent.futures._base.Executor | None = None,
    assistant_thread_context_store: AssistantThreadContextStore | None = None)
    +(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    token_verification_enabled: bool = True,
    client: slack_sdk.web.client.WebClient | None = None,
    before_authorize: Middleware | Callable[..., Any] | None = None,
    authorize: Callable[..., AuthorizeResult] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: OAuthSettings | None = None,
    oauth_flow: OAuthFlow | None = None,
    verification_token: str | None = None,
    listener_executor: concurrent.futures._base.Executor | None = None,
    assistant_thread_context_store: AssistantThreadContextStore | None = None,
    attaching_conversation_kwargs_enabled: bool = True)
    @@ -114,6 +114,7 @@

    Classes

    listener_executor: Optional[Executor] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, + attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -334,6 +335,7 @@

    Classes

    listener_executor = ThreadPoolExecutor(max_workers=5) self._assistant_thread_context_store = assistant_thread_context_store + self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( @@ -818,10 +820,13 @@

    Classes

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -879,6 +884,8 @@

    Classes

    primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -1375,20 +1382,6 @@

    Classes

    # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner - # For AI Agents & Assistants - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=to_event(req.body), # type:ignore[arg-type] - context=req.context, - thread_context_store=self._assistant_thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - @staticmethod def _to_listener_functions( kwargs: dict, @@ -1434,7 +1427,7 @@

    Classes

    CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, # type:ignore[arg-type] + lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, @@ -2222,10 +2215,13 @@

    Args

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -2429,6 +2425,8 @@

    Args

    primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) diff --git a/docs/reference/async_app.html b/docs/reference/async_app.html index 8fd975be9..3494ec289 100644 --- a/docs/reference/async_app.html +++ b/docs/reference/async_app.html @@ -139,7 +139,7 @@

    Class variables

    class AsyncApp -(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    client: slack_sdk.web.async_client.AsyncWebClient | None = None,
    before_authorize: AsyncMiddleware | Callable[..., Awaitable[Any]] | None = None,
    authorize: Callable[..., Awaitable[AuthorizeResult]] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: AsyncOAuthSettings | None = None,
    oauth_flow: AsyncOAuthFlow | None = None,
    verification_token: str | None = None,
    assistant_thread_context_store: AsyncAssistantThreadContextStore | None = None)
    +(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    client: slack_sdk.web.async_client.AsyncWebClient | None = None,
    before_authorize: AsyncMiddleware | Callable[..., Awaitable[Any]] | None = None,
    authorize: Callable[..., Awaitable[AuthorizeResult]] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: AsyncOAuthSettings | None = None,
    oauth_flow: AsyncOAuthFlow | None = None,
    verification_token: str | None = None,
    assistant_thread_context_store: AsyncAssistantThreadContextStore | None = None,
    attaching_conversation_kwargs_enabled: bool = True)
    @@ -183,6 +183,7 @@

    Class variables

    verification_token: Optional[str] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AsyncAssistantThreadContextStore] = None, + attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -403,6 +404,7 @@

    Class variables

    self._async_listeners: List[AsyncListener] = [] self._assistant_thread_context_store = assistant_thread_context_store + self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._async_listener_runner = AsyncioListenerRunner( @@ -656,7 +658,7 @@

    Class variables

    self._framework_logger.debug(debug_checking_listener(listener_name)) if await listener.async_matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - (middleware_resp, next_was_not_called) = await listener.run_async_middleware( + middleware_resp, next_was_not_called = await listener.run_async_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: @@ -906,10 +908,13 @@

    Class variables

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, True, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -970,6 +975,8 @@

    Class variables

    asyncio=True, base_logger=self._base_logger, ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, AsyncMessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -1471,20 +1478,6 @@

    Class variables

    # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner - # For AI Agents & Assistants - if is_assistant_event(req.body): - assistant = AsyncAssistantUtilities( - payload=to_event(req.body), # type:ignore[arg-type] - context=req.context, - thread_context_store=self._assistant_thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - @staticmethod def _to_listener_functions( kwargs: dict, @@ -1535,7 +1528,7 @@

    Class variables

    AsyncCustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, # type:ignore[arg-type] + lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, @@ -1885,7 +1878,7 @@

    Args

    self._framework_logger.debug(debug_checking_listener(listener_name)) if await listener.async_matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first - (middleware_resp, next_was_not_called) = await listener.run_async_middleware( + middleware_resp, next_was_not_called = await listener.run_async_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: @@ -2334,10 +2327,13 @@

    Args

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, True, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -2545,6 +2541,8 @@

    Args

    asyncio=True, base_logger=self._base_logger, ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AsyncAttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, AsyncMessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -3285,7 +3283,7 @@

    Args

    func=is_assistant_thread_started_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3294,7 +3292,7 @@

    Args

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3331,7 +3329,7 @@

    Args

    func=is_user_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3340,7 +3338,7 @@

    Args

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3377,7 +3375,7 @@

    Args

    func=is_bot_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3386,7 +3384,7 @@

    Args

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3423,7 +3421,7 @@

    Args

    func=is_assistant_thread_context_changed_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3432,7 +3430,7 @@

    Args

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3460,14 +3458,14 @@

    Args

    primary_matcher: Union[Callable[..., bool], AsyncListenerMatcher], custom_matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]], ): - return [primary_matcher] + (custom_matchers or []) # type:ignore[operator] + return [primary_matcher] + (custom_matchers or []) # type: ignore[operator] @staticmethod async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict): new_context: dict = payload["assistant_thread"]["context"] await save_thread_context(new_context) - async def async_process( # type:ignore[return] + async def async_process( # type: ignore[return] self, *, req: AsyncBoltRequest, @@ -3487,6 +3485,15 @@

    Args

    if listeners is not None: for listener in listeners: if listener is not None and await listener.async_matches(req=req, resp=resp): + middleware_resp, next_was_not_called = await listener.run_async_middleware(req=req, resp=resp) + if next_was_not_called: + if middleware_resp is not None: + return middleware_resp + # The listener middleware didn't call next(). + # Skip this listener and try the next one. + continue + if middleware_resp is not None: + resp = middleware_resp return await listener_runner.run( request=req, response=resp, @@ -3506,13 +3513,14 @@

    Args

    middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[Logger] = None, ) -> AsyncListener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, AsyncListener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AsyncAttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -3524,7 +3532,7 @@

    Args

    else: listener_matchers.append( build_listener_matcher( - func=matcher, # type:ignore[arg-type] + func=matcher, # type: ignore[arg-type] asyncio=True, base_logger=base_logger, ) @@ -3599,7 +3607,7 @@

    Methods

    func=is_bot_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3608,7 +3616,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3648,13 +3656,14 @@

    Methods

    middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[Logger] = None, ) -> AsyncListener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, AsyncListener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AsyncAttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -3666,7 +3675,7 @@

    Methods

    else: listener_matchers.append( build_listener_matcher( - func=matcher, # type:ignore[arg-type] + func=matcher, # type: ignore[arg-type] asyncio=True, base_logger=base_logger, ) @@ -3707,7 +3716,7 @@

    Methods

    func=is_assistant_thread_context_changed_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3716,7 +3725,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3763,7 +3772,7 @@

    Methods

    func=is_assistant_thread_started_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3772,7 +3781,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3819,7 +3828,7 @@

    Methods

    func=is_user_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -3828,7 +3837,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3898,7 +3907,7 @@

    Inherited members

    # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "AsyncioListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] @@ -3967,7 +3976,7 @@

    Inherited members

    Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id) return self["say"] @property @@ -4060,6 +4069,10 @@

    Inherited members

    def get_thread_context(self) -> Optional[AsyncGetThreadContext]: return self.get("get_thread_context") + @property + def say_stream(self) -> Optional[AsyncSayStream]: + return self.get("say_stream") + @property def save_thread_context(self) -> Optional[AsyncSaveThreadContext]: return self.get("save_thread_context") @@ -4275,7 +4288,7 @@

    Returns

    Expand source code
    @property
    -def listener_runner(self) -> "AsyncioListenerRunner":  # type: ignore[name-defined]
    +def listener_runner(self) -> "AsyncioListenerRunner":
         """The properly configured listener_runner that is available for middleware/listeners."""
         return self["listener_runner"]
    @@ -4365,7 +4378,7 @@

    Returns

    Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id) return self["say"]

    say() function for this request.

    @@ -4383,6 +4396,18 @@

    Returns

    Returns

    Callable say() function

    +
    prop say_streamAsyncSayStream | None
    +
    +
    + +Expand source code + +
    @property
    +def say_stream(self) -> Optional[AsyncSayStream]:
    +    return self.get("say_stream")
    +
    +
    +
    prop set_statusAsyncSetStatus | None
    @@ -4742,14 +4767,10 @@

    Inherited members

    if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: @@ -5231,6 +5252,97 @@

    Class variables

    +
    +class AsyncSayStream +(*,
    client: slack_sdk.web.async_client.AsyncWebClient,
    channel: str | None = None,
    recipient_team_id: str | None = None,
    recipient_user_id: str | None = None,
    thread_ts: str | None = None)
    +
    +
    +
    + +Expand source code + +
    class AsyncSayStream:
    +    client: AsyncWebClient
    +    channel: Optional[str]
    +    recipient_team_id: Optional[str]
    +    recipient_user_id: Optional[str]
    +    thread_ts: Optional[str]
    +
    +    def __init__(
    +        self,
    +        *,
    +        client: AsyncWebClient,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +    ):
    +        self.client = client
    +        self.channel = channel
    +        self.recipient_team_id = recipient_team_id
    +        self.recipient_user_id = recipient_user_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(
    +        self,
    +        *,
    +        buffer_size: Optional[int] = None,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +        **kwargs,
    +    ) -> AsyncChatStream:
    +        """Starts a new chat stream with context."""
    +        channel = channel or self.channel
    +        thread_ts = thread_ts or self.thread_ts
    +        if channel is None:
    +            raise ValueError("say_stream without channel here is unsupported")
    +        if thread_ts is None:
    +            raise ValueError("say_stream without thread_ts here is unsupported")
    +
    +        if buffer_size is not None:
    +            return await self.client.chat_stream(
    +                buffer_size=buffer_size,
    +                channel=channel,
    +                recipient_team_id=recipient_team_id or self.recipient_team_id,
    +                recipient_user_id=recipient_user_id or self.recipient_user_id,
    +                thread_ts=thread_ts,
    +                **kwargs,
    +            )
    +        return await self.client.chat_stream(
    +            channel=channel,
    +            recipient_team_id=recipient_team_id or self.recipient_team_id,
    +            recipient_user_id=recipient_user_id or self.recipient_user_id,
    +            thread_ts=thread_ts,
    +            **kwargs,
    +        )
    +
    +
    +

    Class variables

    +
    +
    var channel : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_team_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_user_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var thread_ts : str | None
    +
    +

    The type of the None singleton.

    +
    +
    +
    class AsyncSetStatus (client: slack_sdk.web.async_client.AsyncWebClient,
    channel_id: str,
    thread_ts: str)
    @@ -5485,6 +5597,7 @@

    respond
  • save_thread_context
  • say
  • +
  • say_stream
  • set_status
  • set_suggested_prompts
  • set_title
  • @@ -5565,6 +5678,16 @@

    AsyncSayStream

    + + +
  • AsyncSetStatus

    • channel_id
    • diff --git a/docs/reference/authorization/authorize_result.html b/docs/reference/authorization/authorize_result.html index d53c5cd5c..6eac3724d 100644 --- a/docs/reference/authorization/authorize_result.html +++ b/docs/reference/authorization/authorize_result.html @@ -48,7 +48,7 @@

      Classes

      class AuthorizeResult -(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: Sequence[str] | str | None = None)
      +(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: str | Sequence[str] | None = None)
      @@ -246,7 +246,7 @@

      Class variables

      Static methods

      -def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse,
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse | None = None)
      +def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
      diff --git a/docs/reference/authorization/index.html b/docs/reference/authorization/index.html index 2fdd1f916..19de311df 100644 --- a/docs/reference/authorization/index.html +++ b/docs/reference/authorization/index.html @@ -75,7 +75,7 @@

      Classes

      class AuthorizeResult -(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: Sequence[str] | str | None = None)
      +(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: str | Sequence[str] | None = None)
      @@ -273,7 +273,7 @@

      Class variables

      Static methods

      -def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse,
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse | None = None)
      +def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
      diff --git a/docs/reference/context/assistant/assistant_utilities.html b/docs/reference/context/assistant/assistant_utilities.html index d446b3c02..40db52284 100644 --- a/docs/reference/context/assistant/assistant_utilities.html +++ b/docs/reference/context/assistant/assistant_utilities.html @@ -91,6 +91,13 @@

      Classes

      @property def set_status(self) -> SetStatus: + warnings.warn( + "AssistantUtilities.set_status is deprecated. " + "Use the set_status argument directly in your listener function " + "or access it via context.set_status instead.", + DeprecationWarning, + stacklevel=2, + ) return SetStatus(self.client, self.channel_id, self.thread_ts) @property @@ -205,6 +212,13 @@

      Instance variables

      @property
       def set_status(self) -> SetStatus:
      +    warnings.warn(
      +        "AssistantUtilities.set_status is deprecated. "
      +        "Use the set_status argument directly in your listener function "
      +        "or access it via context.set_status instead.",
      +        DeprecationWarning,
      +        stacklevel=2,
      +    )
           return SetStatus(self.client, self.channel_id, self.thread_ts)
      diff --git a/docs/reference/context/assistant/async_assistant_utilities.html b/docs/reference/context/assistant/async_assistant_utilities.html index fc3cbbe8b..fc77b80cb 100644 --- a/docs/reference/context/assistant/async_assistant_utilities.html +++ b/docs/reference/context/assistant/async_assistant_utilities.html @@ -91,6 +91,13 @@

      Classes

      @property def set_status(self) -> AsyncSetStatus: + warnings.warn( + "AsyncAssistantUtilities.set_status is deprecated. " + "Use the set_status argument directly in your listener function " + "or access it via context.set_status instead.", + DeprecationWarning, + stacklevel=2, + ) return AsyncSetStatus(self.client, self.channel_id, self.thread_ts) @property @@ -199,6 +206,13 @@

      Instance variables

      @property
       def set_status(self) -> AsyncSetStatus:
      +    warnings.warn(
      +        "AsyncAssistantUtilities.set_status is deprecated. "
      +        "Use the set_status argument directly in your listener function "
      +        "or access it via context.set_status instead.",
      +        DeprecationWarning,
      +        stacklevel=2,
      +    )
           return AsyncSetStatus(self.client, self.channel_id, self.thread_ts)
      diff --git a/docs/reference/context/assistant/thread_context_store/file/index.html b/docs/reference/context/assistant/thread_context_store/file/index.html index 4a5d944e1..cbb4e4db6 100644 --- a/docs/reference/context/assistant/thread_context_store/file/index.html +++ b/docs/reference/context/assistant/thread_context_store/file/index.html @@ -48,7 +48,7 @@

      Classes

      class FileAssistantThreadContextStore -(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts')
      diff --git a/docs/reference/context/async_context.html b/docs/reference/context/async_context.html index 9ce4ebd9e..8fc6d36bf 100644 --- a/docs/reference/context/async_context.html +++ b/docs/reference/context/async_context.html @@ -80,7 +80,7 @@

      Classes

      # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "AsyncioListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] @@ -149,7 +149,7 @@

      Classes

      Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id) return self["say"] @property @@ -242,6 +242,10 @@

      Classes

      def get_thread_context(self) -> Optional[AsyncGetThreadContext]: return self.get("get_thread_context") + @property + def say_stream(self) -> Optional[AsyncSayStream]: + return self.get("say_stream") + @property def save_thread_context(self) -> Optional[AsyncSaveThreadContext]: return self.get("save_thread_context")
      @@ -457,7 +461,7 @@

      Returns

      Expand source code
      @property
      -def listener_runner(self) -> "AsyncioListenerRunner":  # type: ignore[name-defined]
      +def listener_runner(self) -> "AsyncioListenerRunner":
           """The properly configured listener_runner that is available for middleware/listeners."""
           return self["listener_runner"]
      @@ -547,7 +551,7 @@

      Returns

      Callable `say()` function """ if "say" not in self: - self["say"] = AsyncSay(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = AsyncSay(client=self.client, channel=self.channel_id) return self["say"]
  • say() function for this request.

    @@ -565,6 +569,18 @@

    Returns

    Returns

    Callable say() function

    +
    prop say_streamAsyncSayStream | None
    +
    +
    + +Expand source code + +
    @property
    +def say_stream(self) -> Optional[AsyncSayStream]:
    +    return self.get("say_stream")
    +
    +
    +
    prop set_statusAsyncSetStatus | None
    @@ -694,6 +710,7 @@

    respond
  • save_thread_context
  • say
  • +
  • say_stream
  • set_status
  • set_suggested_prompts
  • set_title
  • diff --git a/docs/reference/context/base_context.html b/docs/reference/context/base_context.html index 4a177f8dc..afe571163 100644 --- a/docs/reference/context/base_context.html +++ b/docs/reference/context/base_context.html @@ -89,6 +89,7 @@

    Classes

    "set_status", "set_title", "set_suggested_prompts", + "say_stream", ] # Note that these items are not copyable, so when you add new items to this list, # you must modify ThreadListenerRunner/AsyncioListenerRunner's _build_lazy_request method to pass the values. diff --git a/docs/reference/context/context.html b/docs/reference/context/context.html index 615432502..a7b531c20 100644 --- a/docs/reference/context/context.html +++ b/docs/reference/context/context.html @@ -81,7 +81,7 @@

    Classes

    # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] @@ -150,7 +150,7 @@

    Classes

    Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] @property @@ -243,6 +243,10 @@

    Classes

    def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") + @property + def say_stream(self) -> Optional[SayStream]: + return self.get("say_stream") + @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") @@ -458,7 +462,7 @@

    Returns

    Expand source code
    @property
    -def listener_runner(self) -> "ThreadListenerRunner":  # type: ignore[name-defined]
    +def listener_runner(self) -> "ThreadListenerRunner":
         """The properly configured listener_runner that is available for middleware/listeners."""
         return self["listener_runner"]
    @@ -548,7 +552,7 @@

    Returns

    Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"]

    say() function for this request.

    @@ -566,6 +570,18 @@

    Returns

    Returns

    Callable say() function

    +
    prop say_streamSayStream | None
    +
    +
    + +Expand source code + +
    @property
    +def say_stream(self) -> Optional[SayStream]:
    +    return self.get("say_stream")
    +
    +
    +
    prop set_statusSetStatus | None
    @@ -696,6 +712,7 @@

    respond
  • save_thread_context
  • say
  • +
  • say_stream
  • set_status
  • set_suggested_prompts
  • set_title
  • diff --git a/docs/reference/context/get_thread_context/async_get_thread_context.html b/docs/reference/context/get_thread_context/async_get_thread_context.html index 1c3fc4d6c..967581b50 100644 --- a/docs/reference/context/get_thread_context/async_get_thread_context.html +++ b/docs/reference/context/get_thread_context/async_get_thread_context.html @@ -82,14 +82,10 @@

    Classes

    if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: diff --git a/docs/reference/context/get_thread_context/get_thread_context.html b/docs/reference/context/get_thread_context/get_thread_context.html index 4ac274368..cf2e17a86 100644 --- a/docs/reference/context/get_thread_context/get_thread_context.html +++ b/docs/reference/context/get_thread_context/get_thread_context.html @@ -82,14 +82,10 @@

    Classes

    if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: diff --git a/docs/reference/context/get_thread_context/index.html b/docs/reference/context/get_thread_context/index.html index 13dcd1388..5f9e38e71 100644 --- a/docs/reference/context/get_thread_context/index.html +++ b/docs/reference/context/get_thread_context/index.html @@ -93,14 +93,10 @@

    Classes

    if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: diff --git a/docs/reference/context/index.html b/docs/reference/context/index.html index c761aa47e..ebdfe8aa8 100644 --- a/docs/reference/context/index.html +++ b/docs/reference/context/index.html @@ -89,6 +89,10 @@

    Sub-modules

    +
    slack_bolt.context.say_stream
    +
    +
    +
    slack_bolt.context.set_status
    @@ -145,7 +149,7 @@

    Classes

    # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] @@ -214,7 +218,7 @@

    Classes

    Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] @property @@ -307,6 +311,10 @@

    Classes

    def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") + @property + def say_stream(self) -> Optional[SayStream]: + return self.get("say_stream") + @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") @@ -522,7 +530,7 @@

    Returns

    Expand source code
    @property
    -def listener_runner(self) -> "ThreadListenerRunner":  # type: ignore[name-defined]
    +def listener_runner(self) -> "ThreadListenerRunner":
         """The properly configured listener_runner that is available for middleware/listeners."""
         return self["listener_runner"]
    @@ -612,7 +620,7 @@

    Returns

    Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"]

    slack_bolt.context.say function for this request.

    @@ -630,6 +638,18 @@

    Returns

    Returns

    Callable slack_bolt.context.say function

    +
    prop say_streamSayStream | None
    +
    +
    + +Expand source code + +
    @property
    +def say_stream(self) -> Optional[SayStream]:
    +    return self.get("say_stream")
    +
    +
    +
    prop set_statusSetStatus | None
    @@ -759,6 +779,7 @@

    Inherited members

  • slack_bolt.context.respond
  • slack_bolt.context.save_thread_context
  • slack_bolt.context.say
  • +
  • slack_bolt.context.say_stream
  • slack_bolt.context.set_status
  • slack_bolt.context.set_suggested_prompts
  • slack_bolt.context.set_title
  • @@ -778,6 +799,7 @@

    respond
  • save_thread_context
  • say
  • +
  • say_stream
  • set_status
  • set_suggested_prompts
  • set_title
  • diff --git a/docs/reference/context/say_stream/async_say_stream.html b/docs/reference/context/say_stream/async_say_stream.html new file mode 100644 index 000000000..4010b284d --- /dev/null +++ b/docs/reference/context/say_stream/async_say_stream.html @@ -0,0 +1,174 @@ + + + + + + +slack_bolt.context.say_stream.async_say_stream API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.say_stream.async_say_stream

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class AsyncSayStream +(*,
    client: slack_sdk.web.async_client.AsyncWebClient,
    channel: str | None = None,
    recipient_team_id: str | None = None,
    recipient_user_id: str | None = None,
    thread_ts: str | None = None)
    +
    +
    +
    + +Expand source code + +
    class AsyncSayStream:
    +    client: AsyncWebClient
    +    channel: Optional[str]
    +    recipient_team_id: Optional[str]
    +    recipient_user_id: Optional[str]
    +    thread_ts: Optional[str]
    +
    +    def __init__(
    +        self,
    +        *,
    +        client: AsyncWebClient,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +    ):
    +        self.client = client
    +        self.channel = channel
    +        self.recipient_team_id = recipient_team_id
    +        self.recipient_user_id = recipient_user_id
    +        self.thread_ts = thread_ts
    +
    +    async def __call__(
    +        self,
    +        *,
    +        buffer_size: Optional[int] = None,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +        **kwargs,
    +    ) -> AsyncChatStream:
    +        """Starts a new chat stream with context."""
    +        channel = channel or self.channel
    +        thread_ts = thread_ts or self.thread_ts
    +        if channel is None:
    +            raise ValueError("say_stream without channel here is unsupported")
    +        if thread_ts is None:
    +            raise ValueError("say_stream without thread_ts here is unsupported")
    +
    +        if buffer_size is not None:
    +            return await self.client.chat_stream(
    +                buffer_size=buffer_size,
    +                channel=channel,
    +                recipient_team_id=recipient_team_id or self.recipient_team_id,
    +                recipient_user_id=recipient_user_id or self.recipient_user_id,
    +                thread_ts=thread_ts,
    +                **kwargs,
    +            )
    +        return await self.client.chat_stream(
    +            channel=channel,
    +            recipient_team_id=recipient_team_id or self.recipient_team_id,
    +            recipient_user_id=recipient_user_id or self.recipient_user_id,
    +            thread_ts=thread_ts,
    +            **kwargs,
    +        )
    +
    +
    +

    Class variables

    +
    +
    var channel : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var client : slack_sdk.web.async_client.AsyncWebClient
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_team_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_user_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var thread_ts : str | None
    +
    +

    The type of the None singleton.

    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/reference/context/say_stream/index.html b/docs/reference/context/say_stream/index.html new file mode 100644 index 000000000..645942c72 --- /dev/null +++ b/docs/reference/context/say_stream/index.html @@ -0,0 +1,191 @@ + + + + + + +slack_bolt.context.say_stream API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.say_stream

    +
    +
    +
    +
    +

    Sub-modules

    +
    +
    slack_bolt.context.say_stream.async_say_stream
    +
    +
    +
    +
    slack_bolt.context.say_stream.say_stream
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SayStream +(*,
    client: slack_sdk.web.client.WebClient,
    channel: str | None = None,
    recipient_team_id: str | None = None,
    recipient_user_id: str | None = None,
    thread_ts: str | None = None)
    +
    +
    +
    + +Expand source code + +
    class SayStream:
    +    client: WebClient
    +    channel: Optional[str]
    +    recipient_team_id: Optional[str]
    +    recipient_user_id: Optional[str]
    +    thread_ts: Optional[str]
    +
    +    def __init__(
    +        self,
    +        *,
    +        client: WebClient,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +    ):
    +        self.client = client
    +        self.channel = channel
    +        self.recipient_team_id = recipient_team_id
    +        self.recipient_user_id = recipient_user_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(
    +        self,
    +        *,
    +        buffer_size: Optional[int] = None,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +        **kwargs,
    +    ) -> ChatStream:
    +        """Starts a new chat stream with context."""
    +        channel = channel or self.channel
    +        thread_ts = thread_ts or self.thread_ts
    +        if channel is None:
    +            raise ValueError("say_stream without channel here is unsupported")
    +        if thread_ts is None:
    +            raise ValueError("say_stream without thread_ts here is unsupported")
    +
    +        if buffer_size is not None:
    +            return self.client.chat_stream(
    +                buffer_size=buffer_size,
    +                channel=channel,
    +                recipient_team_id=recipient_team_id or self.recipient_team_id,
    +                recipient_user_id=recipient_user_id or self.recipient_user_id,
    +                thread_ts=thread_ts,
    +                **kwargs,
    +            )
    +        return self.client.chat_stream(
    +            channel=channel,
    +            recipient_team_id=recipient_team_id or self.recipient_team_id,
    +            recipient_user_id=recipient_user_id or self.recipient_user_id,
    +            thread_ts=thread_ts,
    +            **kwargs,
    +        )
    +
    +
    +

    Class variables

    +
    +
    var channel : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_team_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_user_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var thread_ts : str | None
    +
    +

    The type of the None singleton.

    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/reference/context/say_stream/say_stream.html b/docs/reference/context/say_stream/say_stream.html new file mode 100644 index 000000000..784a58bbe --- /dev/null +++ b/docs/reference/context/say_stream/say_stream.html @@ -0,0 +1,174 @@ + + + + + + +slack_bolt.context.say_stream.say_stream API documentation + + + + + + + + + + + +
    +
    +
    +

    Module slack_bolt.context.say_stream.say_stream

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class SayStream +(*,
    client: slack_sdk.web.client.WebClient,
    channel: str | None = None,
    recipient_team_id: str | None = None,
    recipient_user_id: str | None = None,
    thread_ts: str | None = None)
    +
    +
    +
    + +Expand source code + +
    class SayStream:
    +    client: WebClient
    +    channel: Optional[str]
    +    recipient_team_id: Optional[str]
    +    recipient_user_id: Optional[str]
    +    thread_ts: Optional[str]
    +
    +    def __init__(
    +        self,
    +        *,
    +        client: WebClient,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +    ):
    +        self.client = client
    +        self.channel = channel
    +        self.recipient_team_id = recipient_team_id
    +        self.recipient_user_id = recipient_user_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(
    +        self,
    +        *,
    +        buffer_size: Optional[int] = None,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +        **kwargs,
    +    ) -> ChatStream:
    +        """Starts a new chat stream with context."""
    +        channel = channel or self.channel
    +        thread_ts = thread_ts or self.thread_ts
    +        if channel is None:
    +            raise ValueError("say_stream without channel here is unsupported")
    +        if thread_ts is None:
    +            raise ValueError("say_stream without thread_ts here is unsupported")
    +
    +        if buffer_size is not None:
    +            return self.client.chat_stream(
    +                buffer_size=buffer_size,
    +                channel=channel,
    +                recipient_team_id=recipient_team_id or self.recipient_team_id,
    +                recipient_user_id=recipient_user_id or self.recipient_user_id,
    +                thread_ts=thread_ts,
    +                **kwargs,
    +            )
    +        return self.client.chat_stream(
    +            channel=channel,
    +            recipient_team_id=recipient_team_id or self.recipient_team_id,
    +            recipient_user_id=recipient_user_id or self.recipient_user_id,
    +            thread_ts=thread_ts,
    +            **kwargs,
    +        )
    +
    +
    +

    Class variables

    +
    +
    var channel : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_team_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_user_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var thread_ts : str | None
    +
    +

    The type of the None singleton.

    +
    +
    +
    +
    +
    +
    + +
    + + + diff --git a/docs/reference/error/index.html b/docs/reference/error/index.html index 9a9998e63..f57d690e9 100644 --- a/docs/reference/error/index.html +++ b/docs/reference/error/index.html @@ -72,7 +72,7 @@

    Subclasses

    class BoltUnhandledRequestError -(*,
    request: BoltRequest | AsyncBoltRequest,
    current_response: BoltResponse | None,
    last_global_middleware_name: str | None = None)
    +(*,
    request: ForwardRef('BoltRequest') | ForwardRef('AsyncBoltRequest'),
    current_response: ForwardRef('BoltResponse') | None,
    last_global_middleware_name: str | None = None)
    diff --git a/docs/reference/index.html b/docs/reference/index.html index 1c02a8aeb..b2d19719d 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -188,7 +188,7 @@

    Class variables

    class App -(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    token_verification_enabled: bool = True,
    client: slack_sdk.web.client.WebClient | None = None,
    before_authorize: Middleware | Callable[..., Any] | None = None,
    authorize: Callable[..., AuthorizeResult] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: OAuthSettings | None = None,
    oauth_flow: OAuthFlow | None = None,
    verification_token: str | None = None,
    listener_executor: concurrent.futures._base.Executor | None = None,
    assistant_thread_context_store: AssistantThreadContextStore | None = None)
    +(*,
    logger: logging.Logger | None = None,
    name: str | None = None,
    process_before_response: bool = False,
    raise_error_for_unhandled_request: bool = False,
    signing_secret: str | None = None,
    token: str | None = None,
    token_verification_enabled: bool = True,
    client: slack_sdk.web.client.WebClient | None = None,
    before_authorize: Middleware | Callable[..., Any] | None = None,
    authorize: Callable[..., AuthorizeResult] | None = None,
    user_facing_authorize_error_message: str | None = None,
    installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
    installation_store_bot_only: bool | None = None,
    request_verification_enabled: bool = True,
    ignoring_self_events_enabled: bool = True,
    ignoring_self_assistant_message_events_enabled: bool = True,
    ssl_check_enabled: bool = True,
    url_verification_enabled: bool = True,
    attaching_function_token_enabled: bool = True,
    oauth_settings: OAuthSettings | None = None,
    oauth_flow: OAuthFlow | None = None,
    verification_token: str | None = None,
    listener_executor: concurrent.futures._base.Executor | None = None,
    assistant_thread_context_store: AssistantThreadContextStore | None = None,
    attaching_conversation_kwargs_enabled: bool = True)
    @@ -235,6 +235,7 @@

    Class variables

    listener_executor: Optional[Executor] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, + attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. @@ -455,6 +456,7 @@

    Class variables

    listener_executor = ThreadPoolExecutor(max_workers=5) self._assistant_thread_context_store = assistant_thread_context_store + self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( @@ -939,10 +941,13 @@

    Class variables

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -1000,6 +1005,8 @@

    Class variables

    primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -1496,20 +1503,6 @@

    Class variables

    # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner - # For AI Agents & Assistants - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=to_event(req.body), # type:ignore[arg-type] - context=req.context, - thread_context_store=self._assistant_thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_status"] = assistant.set_status - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - @staticmethod def _to_listener_functions( kwargs: dict, @@ -1555,7 +1548,7 @@

    Class variables

    CustomListener( app_name=self.name, ack_function=functions.pop(0), - lazy_functions=functions, # type:ignore[arg-type] + lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, @@ -2343,10 +2336,13 @@

    Args

    middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ + middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ @@ -2550,6 +2546,8 @@

    Args

    primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) + if self._attaching_conversation_kwargs_enabled: + middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) @@ -3179,7 +3177,7 @@

    Args

    class Args -(*,
    logger: logging.Logger,
    client: slack_sdk.web.client.WebClient,
    req: BoltRequest,
    resp: BoltResponse,
    context: BoltContext,
    body: Dict[str, Any],
    payload: Dict[str, Any],
    options: Dict[str, Any] | None = None,
    shortcut: Dict[str, Any] | None = None,
    action: Dict[str, Any] | None = None,
    view: Dict[str, Any] | None = None,
    command: Dict[str, Any] | None = None,
    event: Dict[str, Any] | None = None,
    message: Dict[str, Any] | None = None,
    ack: Ack,
    say: Say,
    respond: Respond,
    complete: Complete,
    fail: Fail,
    set_status: SetStatus | None = None,
    set_title: SetTitle | None = None,
    set_suggested_prompts: SetSuggestedPrompts | None = None,
    get_thread_context: GetThreadContext | None = None,
    save_thread_context: SaveThreadContext | None = None,
    next: Callable[[], None],
    **kwargs)
    +(*,
    logger: logging.Logger,
    client: slack_sdk.web.client.WebClient,
    req: BoltRequest,
    resp: BoltResponse,
    context: BoltContext,
    body: Dict[str, Any],
    payload: Dict[str, Any],
    options: Dict[str, Any] | None = None,
    shortcut: Dict[str, Any] | None = None,
    action: Dict[str, Any] | None = None,
    view: Dict[str, Any] | None = None,
    command: Dict[str, Any] | None = None,
    event: Dict[str, Any] | None = None,
    message: Dict[str, Any] | None = None,
    ack: Ack,
    say: Say,
    respond: Respond,
    complete: Complete,
    fail: Fail,
    set_status: SetStatus | None = None,
    set_title: SetTitle | None = None,
    set_suggested_prompts: SetSuggestedPrompts | None = None,
    get_thread_context: GetThreadContext | None = None,
    save_thread_context: SaveThreadContext | None = None,
    say_stream: SayStream | None = None,
    next: Callable[[], None],
    **kwargs)
    @@ -3270,6 +3268,8 @@

    Args

    """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" + say_stream: Optional[SayStream] + """`say_stream()` utility function for conversations, AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -3303,6 +3303,7 @@

    Args

    set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, + say_stream: Optional[SayStream] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method @@ -3336,6 +3337,7 @@

    Args

    self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context + self.say_stream = say_stream self.next: Callable[[], None] = next self.next_: Callable[[], None] = next @@ -3459,6 +3461,10 @@

    Class variables

    say() utility function, which calls chat.postMessage API with the associated channel ID

    +
    var say_streamSayStream | None
    +
    +

    say_stream() utility function for conversations, AI Agents & Assistants

    +
    var set_statusSetStatus | None

    set_status() utility function for AI Agents & Assistants

    @@ -3531,7 +3537,7 @@

    Class variables

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3570,7 +3576,7 @@

    Class variables

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3609,7 +3615,7 @@

    Class variables

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3648,7 +3654,7 @@

    Class variables

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3678,13 +3684,13 @@

    Class variables

    ): return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + ( custom_matchers or [] - ) # type:ignore[operator] + ) # type: ignore[operator] @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) - def process( # type:ignore[return] + def process( # type: ignore[return] self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse] ) -> Optional[BoltResponse]: if self._thread_context_changed_listeners is None: @@ -3700,6 +3706,15 @@

    Class variables

    if listeners is not None: for listener in listeners: if listener.matches(req=req, resp=resp): + middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) + if next_was_not_called: + if middleware_resp is not None: + return middleware_resp + # The listener middleware didn't call next(). + # Skip this listener and try the next one. + continue + if middleware_resp is not None: + resp = middleware_resp return listener_runner.run( request=req, response=resp, @@ -3719,13 +3734,14 @@

    Class variables

    middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -3734,7 +3750,7 @@

    Class variables

    for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) - elif isinstance(matcher, Callable): # type:ignore[arg-type] + elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, @@ -3813,7 +3829,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3853,13 +3869,14 @@

    Methods

    middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -3868,7 +3885,7 @@

    Methods

    for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) - elif isinstance(matcher, Callable): # type:ignore[arg-type] + elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, @@ -3914,7 +3931,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -3963,7 +3980,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -4012,7 +4029,7 @@

    Methods

    self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -4185,7 +4202,7 @@

    Methods

    # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] @@ -4254,7 +4271,7 @@

    Methods

    Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] @property @@ -4347,6 +4364,10 @@

    Methods

    def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") + @property + def say_stream(self) -> Optional[SayStream]: + return self.get("say_stream") + @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") @@ -4562,7 +4583,7 @@

    Returns

    Expand source code
    @property
    -def listener_runner(self) -> "ThreadListenerRunner":  # type: ignore[name-defined]
    +def listener_runner(self) -> "ThreadListenerRunner":
         """The properly configured listener_runner that is available for middleware/listeners."""
         return self["listener_runner"]
    @@ -4652,7 +4673,7 @@

    Returns

    Callable `say()` function """ if "say" not in self: - self["say"] = Say(client=self.client, channel=self.channel_id, thread_ts=self.thread_ts) + self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"]

    say() function for this request.

    @@ -4670,6 +4691,18 @@

    Returns

    Returns

    Callable say() function

    +
    prop say_streamSayStream | None
    +
    +
    + +Expand source code + +
    @property
    +def say_stream(self) -> Optional[SayStream]:
    +    return self.get("say_stream")
    +
    +
    +
    prop set_statusSetStatus | None
    @@ -5304,7 +5337,7 @@

    Returns

    class FileAssistantThreadContextStore -(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts')
    @@ -5843,6 +5876,97 @@

    Class variables

    +
    +class SayStream +(*,
    client: slack_sdk.web.client.WebClient,
    channel: str | None = None,
    recipient_team_id: str | None = None,
    recipient_user_id: str | None = None,
    thread_ts: str | None = None)
    +
    +
    +
    + +Expand source code + +
    class SayStream:
    +    client: WebClient
    +    channel: Optional[str]
    +    recipient_team_id: Optional[str]
    +    recipient_user_id: Optional[str]
    +    thread_ts: Optional[str]
    +
    +    def __init__(
    +        self,
    +        *,
    +        client: WebClient,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +    ):
    +        self.client = client
    +        self.channel = channel
    +        self.recipient_team_id = recipient_team_id
    +        self.recipient_user_id = recipient_user_id
    +        self.thread_ts = thread_ts
    +
    +    def __call__(
    +        self,
    +        *,
    +        buffer_size: Optional[int] = None,
    +        channel: Optional[str] = None,
    +        recipient_team_id: Optional[str] = None,
    +        recipient_user_id: Optional[str] = None,
    +        thread_ts: Optional[str] = None,
    +        **kwargs,
    +    ) -> ChatStream:
    +        """Starts a new chat stream with context."""
    +        channel = channel or self.channel
    +        thread_ts = thread_ts or self.thread_ts
    +        if channel is None:
    +            raise ValueError("say_stream without channel here is unsupported")
    +        if thread_ts is None:
    +            raise ValueError("say_stream without thread_ts here is unsupported")
    +
    +        if buffer_size is not None:
    +            return self.client.chat_stream(
    +                buffer_size=buffer_size,
    +                channel=channel,
    +                recipient_team_id=recipient_team_id or self.recipient_team_id,
    +                recipient_user_id=recipient_user_id or self.recipient_user_id,
    +                thread_ts=thread_ts,
    +                **kwargs,
    +            )
    +        return self.client.chat_stream(
    +            channel=channel,
    +            recipient_team_id=recipient_team_id or self.recipient_team_id,
    +            recipient_user_id=recipient_user_id or self.recipient_user_id,
    +            thread_ts=thread_ts,
    +            **kwargs,
    +        )
    +
    +
    +

    Class variables

    +
    +
    var channel : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var client : slack_sdk.web.client.WebClient
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_team_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var recipient_user_id : str | None
    +
    +

    The type of the None singleton.

    +
    +
    var thread_ts : str | None
    +
    +

    The type of the None singleton.

    +
    +
    +
    class SetStatus (client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) @@ -6110,6 +6234,7 @@

    Args

    response
  • save_thread_context
  • say
  • +
  • say_stream
  • set_status
  • set_suggested_prompts
  • set_title
  • @@ -6157,6 +6282,7 @@

    BoltC
  • respond
  • save_thread_context
  • say
  • +
  • say_stream
  • set_status
  • set_suggested_prompts
  • set_title
  • @@ -6262,6 +6388,16 @@

    Say

  • +

    SayStream

    + +
  • +
  • SetStatus

    • channel_id
    • diff --git a/docs/reference/kwargs_injection/args.html b/docs/reference/kwargs_injection/args.html index 4d03687d1..bbba71eb8 100644 --- a/docs/reference/kwargs_injection/args.html +++ b/docs/reference/kwargs_injection/args.html @@ -48,7 +48,7 @@

      Classes

      class Args -(*,
      logger: logging.Logger,
      client: slack_sdk.web.client.WebClient,
      req: BoltRequest,
      resp: BoltResponse,
      context: BoltContext,
      body: Dict[str, Any],
      payload: Dict[str, Any],
      options: Dict[str, Any] | None = None,
      shortcut: Dict[str, Any] | None = None,
      action: Dict[str, Any] | None = None,
      view: Dict[str, Any] | None = None,
      command: Dict[str, Any] | None = None,
      event: Dict[str, Any] | None = None,
      message: Dict[str, Any] | None = None,
      ack: Ack,
      say: Say,
      respond: Respond,
      complete: Complete,
      fail: Fail,
      set_status: SetStatus | None = None,
      set_title: SetTitle | None = None,
      set_suggested_prompts: SetSuggestedPrompts | None = None,
      get_thread_context: GetThreadContext | None = None,
      save_thread_context: SaveThreadContext | None = None,
      next: Callable[[], None],
      **kwargs)
      +(*,
      logger: logging.Logger,
      client: slack_sdk.web.client.WebClient,
      req: BoltRequest,
      resp: BoltResponse,
      context: BoltContext,
      body: Dict[str, Any],
      payload: Dict[str, Any],
      options: Dict[str, Any] | None = None,
      shortcut: Dict[str, Any] | None = None,
      action: Dict[str, Any] | None = None,
      view: Dict[str, Any] | None = None,
      command: Dict[str, Any] | None = None,
      event: Dict[str, Any] | None = None,
      message: Dict[str, Any] | None = None,
      ack: Ack,
      say: Say,
      respond: Respond,
      complete: Complete,
      fail: Fail,
      set_status: SetStatus | None = None,
      set_title: SetTitle | None = None,
      set_suggested_prompts: SetSuggestedPrompts | None = None,
      get_thread_context: GetThreadContext | None = None,
      save_thread_context: SaveThreadContext | None = None,
      say_stream: SayStream | None = None,
      next: Callable[[], None],
      **kwargs)
      @@ -139,6 +139,8 @@

      Classes

      """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" + say_stream: Optional[SayStream] + """`say_stream()` utility function for conversations, AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -172,6 +174,7 @@

      Classes

      set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, + say_stream: Optional[SayStream] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method @@ -205,6 +208,7 @@

      Classes

      self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context + self.say_stream = say_stream self.next: Callable[[], None] = next self.next_: Callable[[], None] = next
      @@ -328,6 +332,10 @@

      Class variables

      say() utility function, which calls chat.postMessage API with the associated channel ID

      +
      var say_streamSayStream | None
      +
      +

      say_stream() utility function for conversations, AI Agents & Assistants

      +
      var set_statusSetStatus | None

      set_status() utility function for AI Agents & Assistants

      @@ -391,6 +399,7 @@

      response
    • save_thread_context
    • say
    • +
    • say_stream
    • set_status
    • set_suggested_prompts
    • set_title
    • diff --git a/docs/reference/kwargs_injection/async_args.html b/docs/reference/kwargs_injection/async_args.html index 959f35a43..5b0e7b70e 100644 --- a/docs/reference/kwargs_injection/async_args.html +++ b/docs/reference/kwargs_injection/async_args.html @@ -48,7 +48,7 @@

      Classes

      class AsyncArgs -(*,
      logger: logging.Logger,
      client: slack_sdk.web.async_client.AsyncWebClient,
      req: AsyncBoltRequest,
      resp: BoltResponse,
      context: AsyncBoltContext,
      body: Dict[str, Any],
      payload: Dict[str, Any],
      options: Dict[str, Any] | None = None,
      shortcut: Dict[str, Any] | None = None,
      action: Dict[str, Any] | None = None,
      view: Dict[str, Any] | None = None,
      command: Dict[str, Any] | None = None,
      event: Dict[str, Any] | None = None,
      message: Dict[str, Any] | None = None,
      ack: AsyncAck,
      say: AsyncSay,
      respond: AsyncRespond,
      complete: AsyncComplete,
      fail: AsyncFail,
      set_status: AsyncSetStatus | None = None,
      set_title: AsyncSetTitle | None = None,
      set_suggested_prompts: AsyncSetSuggestedPrompts | None = None,
      get_thread_context: AsyncGetThreadContext | None = None,
      save_thread_context: AsyncSaveThreadContext | None = None,
      next: Callable[[], Awaitable[None]],
      **kwargs)
      +(*,
      logger: logging.Logger,
      client: slack_sdk.web.async_client.AsyncWebClient,
      req: AsyncBoltRequest,
      resp: BoltResponse,
      context: AsyncBoltContext,
      body: Dict[str, Any],
      payload: Dict[str, Any],
      options: Dict[str, Any] | None = None,
      shortcut: Dict[str, Any] | None = None,
      action: Dict[str, Any] | None = None,
      view: Dict[str, Any] | None = None,
      command: Dict[str, Any] | None = None,
      event: Dict[str, Any] | None = None,
      message: Dict[str, Any] | None = None,
      ack: AsyncAck,
      say: AsyncSay,
      respond: AsyncRespond,
      complete: AsyncComplete,
      fail: AsyncFail,
      set_status: AsyncSetStatus | None = None,
      set_title: AsyncSetTitle | None = None,
      set_suggested_prompts: AsyncSetSuggestedPrompts | None = None,
      get_thread_context: AsyncGetThreadContext | None = None,
      save_thread_context: AsyncSaveThreadContext | None = None,
      say_stream: AsyncSayStream | None = None,
      next: Callable[[], Awaitable[None]],
      **kwargs)
      @@ -139,6 +139,8 @@

      Classes

      """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[AsyncSaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" + say_stream: Optional[AsyncSayStream] + """`say_stream()` utility function for AI Agents & Assistants""" # middleware next: Callable[[], Awaitable[None]] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -172,6 +174,7 @@

      Classes

      set_suggested_prompts: Optional[AsyncSetSuggestedPrompts] = None, get_thread_context: Optional[AsyncGetThreadContext] = None, save_thread_context: Optional[AsyncSaveThreadContext] = None, + say_stream: Optional[AsyncSayStream] = None, next: Callable[[], Awaitable[None]], **kwargs, # noqa ): @@ -202,6 +205,7 @@

      Classes

      self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context + self.say_stream = say_stream self.next: Callable[[], Awaitable[None]] = next self.next_: Callable[[], Awaitable[None]] = next @@ -325,6 +329,10 @@

      Class variables

      say() utility function, which calls chat.postMessage API with the associated channel ID

      +
      var say_streamAsyncSayStream | None
      +
      +

      say_stream() utility function for AI Agents & Assistants

      +
      var set_statusAsyncSetStatus | None

      set_status() utility function for AI Agents & Assistants

      @@ -388,6 +396,7 @@

      response
    • save_thread_context
    • say
    • +
    • say_stream
    • set_status
    • set_suggested_prompts
    • set_title
    • diff --git a/docs/reference/kwargs_injection/async_utils.html b/docs/reference/kwargs_injection/async_utils.html index 80952518d..7af3a7679 100644 --- a/docs/reference/kwargs_injection/async_utils.html +++ b/docs/reference/kwargs_injection/async_utils.html @@ -63,7 +63,7 @@

      Functions

      error: Optional[Exception] = None, # for error handlers next_keys_required: bool = True, # False for listeners / middleware / error handlers ) -> Dict[str, Any]: - all_available_args = { + all_available_args: Dict[str, Any] = { "logger": logger, "client": request.context.client, "req": request, @@ -92,6 +92,7 @@

      Functions

      "set_suggested_prompts": request.context.set_suggested_prompts, "get_thread_context": request.context.get_thread_context, "save_thread_context": request.context.save_thread_context, + "say_stream": request.context.say_stream, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function @@ -136,7 +137,7 @@

      Functions

      for name in required_arg_names: if name == "args": if isinstance(request, AsyncBoltRequest): - kwargs[name] = AsyncArgs(**all_available_args) # type: ignore[arg-type] + kwargs[name] = AsyncArgs(**all_available_args) else: logger.warning(f"Unknown Request object type detected ({type(request)})") diff --git a/docs/reference/kwargs_injection/index.html b/docs/reference/kwargs_injection/index.html index de7ef4a0a..cb17cea5d 100644 --- a/docs/reference/kwargs_injection/index.html +++ b/docs/reference/kwargs_injection/index.html @@ -85,7 +85,7 @@

      Functions

      error: Optional[Exception] = None, # for error handlers next_keys_required: bool = True, # False for listeners / middleware / error handlers ) -> Dict[str, Any]: - all_available_args = { + all_available_args: Dict[str, Any] = { "logger": logger, "client": request.context.client, "req": request, @@ -113,6 +113,7 @@

      Functions

      "set_title": request.context.set_title, "set_suggested_prompts": request.context.set_suggested_prompts, "save_thread_context": request.context.save_thread_context, + "say_stream": request.context.say_stream, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function @@ -157,7 +158,7 @@

      Functions

      for name in required_arg_names: if name == "args": if isinstance(request, BoltRequest): - kwargs[name] = Args(**all_available_args) # type: ignore[arg-type] + kwargs[name] = Args(**all_available_args) else: logger.warning(f"Unknown Request object type detected ({type(request)})") @@ -175,7 +176,7 @@

      Classes

      class Args -(*,
      logger: logging.Logger,
      client: slack_sdk.web.client.WebClient,
      req: BoltRequest,
      resp: BoltResponse,
      context: BoltContext,
      body: Dict[str, Any],
      payload: Dict[str, Any],
      options: Dict[str, Any] | None = None,
      shortcut: Dict[str, Any] | None = None,
      action: Dict[str, Any] | None = None,
      view: Dict[str, Any] | None = None,
      command: Dict[str, Any] | None = None,
      event: Dict[str, Any] | None = None,
      message: Dict[str, Any] | None = None,
      ack: Ack,
      say: Say,
      respond: Respond,
      complete: Complete,
      fail: Fail,
      set_status: SetStatus | None = None,
      set_title: SetTitle | None = None,
      set_suggested_prompts: SetSuggestedPrompts | None = None,
      get_thread_context: GetThreadContext | None = None,
      save_thread_context: SaveThreadContext | None = None,
      next: Callable[[], None],
      **kwargs)
      +(*,
      logger: logging.Logger,
      client: slack_sdk.web.client.WebClient,
      req: BoltRequest,
      resp: BoltResponse,
      context: BoltContext,
      body: Dict[str, Any],
      payload: Dict[str, Any],
      options: Dict[str, Any] | None = None,
      shortcut: Dict[str, Any] | None = None,
      action: Dict[str, Any] | None = None,
      view: Dict[str, Any] | None = None,
      command: Dict[str, Any] | None = None,
      event: Dict[str, Any] | None = None,
      message: Dict[str, Any] | None = None,
      ack: Ack,
      say: Say,
      respond: Respond,
      complete: Complete,
      fail: Fail,
      set_status: SetStatus | None = None,
      set_title: SetTitle | None = None,
      set_suggested_prompts: SetSuggestedPrompts | None = None,
      get_thread_context: GetThreadContext | None = None,
      save_thread_context: SaveThreadContext | None = None,
      say_stream: SayStream | None = None,
      next: Callable[[], None],
      **kwargs)
      @@ -266,6 +267,8 @@

      Classes

      """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" + say_stream: Optional[SayStream] + """`say_stream()` utility function for conversations, AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" @@ -299,6 +302,7 @@

      Classes

      set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, + say_stream: Optional[SayStream] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method @@ -332,6 +336,7 @@

      Classes

      self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context + self.say_stream = say_stream self.next: Callable[[], None] = next self.next_: Callable[[], None] = next
      @@ -455,6 +460,10 @@

      Class variables

      say() utility function, which calls chat.postMessage API with the associated channel ID

      +
      var say_streamSayStream | None
      +
      +

      say_stream() utility function for conversations, AI Agents & Assistants

      +
      var set_statusSetStatus | None

      set_status() utility function for AI Agents & Assistants

      @@ -531,6 +540,7 @@

      response
    • save_thread_context
    • say
    • +
    • say_stream
    • set_status
    • set_suggested_prompts
    • set_title
    • diff --git a/docs/reference/kwargs_injection/utils.html b/docs/reference/kwargs_injection/utils.html index 2e6ecd001..0289fd410 100644 --- a/docs/reference/kwargs_injection/utils.html +++ b/docs/reference/kwargs_injection/utils.html @@ -63,7 +63,7 @@

      Functions

      error: Optional[Exception] = None, # for error handlers next_keys_required: bool = True, # False for listeners / middleware / error handlers ) -> Dict[str, Any]: - all_available_args = { + all_available_args: Dict[str, Any] = { "logger": logger, "client": request.context.client, "req": request, @@ -91,6 +91,7 @@

      Functions

      "set_title": request.context.set_title, "set_suggested_prompts": request.context.set_suggested_prompts, "save_thread_context": request.context.save_thread_context, + "say_stream": request.context.say_stream, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function @@ -135,7 +136,7 @@

      Functions

      for name in required_arg_names: if name == "args": if isinstance(request, BoltRequest): - kwargs[name] = Args(**all_available_args) # type: ignore[arg-type] + kwargs[name] = Args(**all_available_args) else: logger.warning(f"Unknown Request object type detected ({type(request)})") diff --git a/docs/reference/listener/async_listener_error_handler.html b/docs/reference/listener/async_listener_error_handler.html index 1f3789c40..ebee4441a 100644 --- a/docs/reference/listener/async_listener_error_handler.html +++ b/docs/reference/listener/async_listener_error_handler.html @@ -77,9 +77,10 @@

      Classes

      ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body

      Ancestors

      diff --git a/docs/reference/listener/listener_error_handler.html b/docs/reference/listener/listener_error_handler.html index c9f7c2ccd..e344b15cb 100644 --- a/docs/reference/listener/listener_error_handler.html +++ b/docs/reference/listener/listener_error_handler.html @@ -77,9 +77,10 @@

      Classes

      ) returned_response = self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body

      Ancestors

      diff --git a/docs/reference/logger/messages.html b/docs/reference/logger/messages.html index e69b45fc9..1072e6479 100644 --- a/docs/reference/logger/messages.html +++ b/docs/reference/logger/messages.html @@ -409,7 +409,7 @@

      Functions

      -def warning_unhandled_by_global_middleware(name: str,
      req: BoltRequest | AsyncBoltRequest) ‑> str
      +def warning_unhandled_by_global_middleware(name: str,
      req: BoltRequest | ForwardRef('AsyncBoltRequest')) ‑> str
      @@ -427,7 +427,7 @@

      Functions

      -def warning_unhandled_request(req: BoltRequest | AsyncBoltRequest) ‑> str +def warning_unhandled_request(req: BoltRequest | ForwardRef('AsyncBoltRequest')) ‑> str
      diff --git a/docs/reference/middleware/assistant/assistant.html b/docs/reference/middleware/assistant/assistant.html index d1184c407..946416d62 100644 --- a/docs/reference/middleware/assistant/assistant.html +++ b/docs/reference/middleware/assistant/assistant.html @@ -96,7 +96,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -135,7 +135,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -174,7 +174,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -213,7 +213,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -243,13 +243,13 @@

      Classes

      ): return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + ( custom_matchers or [] - ) # type:ignore[operator] + ) # type: ignore[operator] @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) - def process( # type:ignore[return] + def process( # type: ignore[return] self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse] ) -> Optional[BoltResponse]: if self._thread_context_changed_listeners is None: @@ -265,6 +265,15 @@

      Classes

      if listeners is not None: for listener in listeners: if listener.matches(req=req, resp=resp): + middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) + if next_was_not_called: + if middleware_resp is not None: + return middleware_resp + # The listener middleware didn't call next(). + # Skip this listener and try the next one. + continue + if middleware_resp is not None: + resp = middleware_resp return listener_runner.run( request=req, response=resp, @@ -284,13 +293,14 @@

      Classes

      middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -299,7 +309,7 @@

      Classes

      for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) - elif isinstance(matcher, Callable): # type:ignore[arg-type] + elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, @@ -378,7 +388,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -418,13 +428,14 @@

      Methods

      middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -433,7 +444,7 @@

      Methods

      for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) - elif isinstance(matcher, Callable): # type:ignore[arg-type] + elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, @@ -479,7 +490,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -528,7 +539,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -577,7 +588,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func diff --git a/docs/reference/middleware/assistant/async_assistant.html b/docs/reference/middleware/assistant/async_assistant.html index 2faf0e34b..748be2cbf 100644 --- a/docs/reference/middleware/assistant/async_assistant.html +++ b/docs/reference/middleware/assistant/async_assistant.html @@ -94,7 +94,7 @@

      Classes

      func=is_assistant_thread_started_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -103,7 +103,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -140,7 +140,7 @@

      Classes

      func=is_user_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -149,7 +149,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -186,7 +186,7 @@

      Classes

      func=is_bot_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -195,7 +195,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -232,7 +232,7 @@

      Classes

      func=is_assistant_thread_context_changed_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -241,7 +241,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -269,14 +269,14 @@

      Classes

      primary_matcher: Union[Callable[..., bool], AsyncListenerMatcher], custom_matchers: Optional[Union[Callable[..., bool], AsyncListenerMatcher]], ): - return [primary_matcher] + (custom_matchers or []) # type:ignore[operator] + return [primary_matcher] + (custom_matchers or []) # type: ignore[operator] @staticmethod async def default_thread_context_changed(save_thread_context: AsyncSaveThreadContext, payload: dict): new_context: dict = payload["assistant_thread"]["context"] await save_thread_context(new_context) - async def async_process( # type:ignore[return] + async def async_process( # type: ignore[return] self, *, req: AsyncBoltRequest, @@ -296,6 +296,15 @@

      Classes

      if listeners is not None: for listener in listeners: if listener is not None and await listener.async_matches(req=req, resp=resp): + middleware_resp, next_was_not_called = await listener.run_async_middleware(req=req, resp=resp) + if next_was_not_called: + if middleware_resp is not None: + return middleware_resp + # The listener middleware didn't call next(). + # Skip this listener and try the next one. + continue + if middleware_resp is not None: + resp = middleware_resp return await listener_runner.run( request=req, response=resp, @@ -315,13 +324,14 @@

      Classes

      middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[Logger] = None, ) -> AsyncListener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, AsyncListener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AsyncAttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -333,7 +343,7 @@

      Classes

      else: listener_matchers.append( build_listener_matcher( - func=matcher, # type:ignore[arg-type] + func=matcher, # type: ignore[arg-type] asyncio=True, base_logger=base_logger, ) @@ -408,7 +418,7 @@

      Methods

      func=is_bot_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -417,7 +427,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -457,13 +467,14 @@

      Methods

      middleware: Optional[List[AsyncMiddleware]] = None, base_logger: Optional[Logger] = None, ) -> AsyncListener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, AsyncListener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AsyncAttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -475,7 +486,7 @@

      Methods

      else: listener_matchers.append( build_listener_matcher( - func=matcher, # type:ignore[arg-type] + func=matcher, # type: ignore[arg-type] asyncio=True, base_logger=base_logger, ) @@ -516,7 +527,7 @@

      Methods

      func=is_assistant_thread_context_changed_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -525,7 +536,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -572,7 +583,7 @@

      Methods

      func=is_assistant_thread_started_event, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -581,7 +592,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -628,7 +639,7 @@

      Methods

      func=is_user_message_event_in_assistant_thread, asyncio=True, base_logger=self.base_logger, - ), # type:ignore[arg-type] + ), # type: ignore[arg-type] matchers, ) if is_used_without_argument(args): @@ -637,7 +648,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func diff --git a/docs/reference/middleware/assistant/index.html b/docs/reference/middleware/assistant/index.html index 92f405cad..e9fce8d64 100644 --- a/docs/reference/middleware/assistant/index.html +++ b/docs/reference/middleware/assistant/index.html @@ -107,7 +107,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -146,7 +146,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -185,7 +185,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -224,7 +224,7 @@

      Classes

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -254,13 +254,13 @@

      Classes

      ): return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + ( custom_matchers or [] - ) # type:ignore[operator] + ) # type: ignore[operator] @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) - def process( # type:ignore[return] + def process( # type: ignore[return] self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse] ) -> Optional[BoltResponse]: if self._thread_context_changed_listeners is None: @@ -276,6 +276,15 @@

      Classes

      if listeners is not None: for listener in listeners: if listener.matches(req=req, resp=resp): + middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) + if next_was_not_called: + if middleware_resp is not None: + return middleware_resp + # The listener middleware didn't call next(). + # Skip this listener and try the next one. + continue + if middleware_resp is not None: + resp = middleware_resp return listener_runner.run( request=req, response=resp, @@ -295,13 +304,14 @@

      Classes

      middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -310,7 +320,7 @@

      Classes

      for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) - elif isinstance(matcher, Callable): # type:ignore[arg-type] + elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, @@ -389,7 +399,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -429,13 +439,14 @@

      Methods

      middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: - if isinstance(listener_or_functions, Callable): # type:ignore[arg-type] - listener_or_functions = [listener_or_functions] # type:ignore[list-item] + if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] + listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] + middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) @@ -444,7 +455,7 @@

      Methods

      for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) - elif isinstance(matcher, Callable): # type:ignore[arg-type] + elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, @@ -490,7 +501,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -539,7 +550,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func @@ -588,7 +599,7 @@

      Methods

      self.build_listener( listener_or_functions=func, matchers=all_matchers, - middleware=middleware, # type:ignore[arg-type] + middleware=middleware, # type: ignore[arg-type] ) ) return func diff --git a/docs/reference/middleware/async_builtins.html b/docs/reference/middleware/async_builtins.html index d32deff15..1ddea9222 100644 --- a/docs/reference/middleware/async_builtins.html +++ b/docs/reference/middleware/async_builtins.html @@ -46,6 +46,82 @@

      Module slack_bolt.middleware.async_builtins

      Classes

      +
      +class AsyncAttachingConversationKwargs +(thread_context_store: AsyncAssistantThreadContextStore | None = None) +
      +
      +
      + +Expand source code + +
      class AsyncAttachingConversationKwargs(AsyncMiddleware):
      +
      +    thread_context_store: Optional[AsyncAssistantThreadContextStore]
      +
      +    def __init__(self, thread_context_store: Optional[AsyncAssistantThreadContextStore] = None):
      +        self.thread_context_store = thread_context_store
      +
      +    async def async_process(
      +        self,
      +        *,
      +        req: AsyncBoltRequest,
      +        resp: BoltResponse,
      +        next: Callable[[], Awaitable[BoltResponse]],
      +    ) -> Optional[BoltResponse]:
      +        event = to_event(req.body)
      +        if event is not None:
      +            if is_assistant_event(req.body):
      +                assistant = AsyncAssistantUtilities(
      +                    payload=event,
      +                    context=req.context,
      +                    thread_context_store=self.thread_context_store,
      +                )
      +                req.context["say"] = assistant.say
      +                req.context["set_title"] = assistant.set_title
      +                req.context["set_suggested_prompts"] = assistant.set_suggested_prompts
      +                req.context["get_thread_context"] = assistant.get_thread_context
      +                req.context["save_thread_context"] = assistant.save_thread_context
      +
      +            # TODO: in the future we might want to introduce a "proper" extract_ts utility
      +            thread_ts = req.context.thread_ts or event.get("ts")
      +            if req.context.channel_id and thread_ts:
      +                req.context["set_status"] = AsyncSetStatus(
      +                    client=req.context.client,
      +                    channel_id=req.context.channel_id,
      +                    thread_ts=thread_ts,
      +                )
      +                req.context["say_stream"] = AsyncSayStream(
      +                    client=req.context.client,
      +                    channel=req.context.channel_id,
      +                    recipient_team_id=req.context.team_id or req.context.enterprise_id,
      +                    recipient_user_id=req.context.user_id,
      +                    thread_ts=thread_ts,
      +                )
      +        return await next()
      +
      +

      A middleware can process request data before other middleware and listener functions.

      +

      Ancestors

      + +

      Class variables

      +
      +
      var thread_context_storeAsyncAssistantThreadContextStore | None
      +
      +

      The type of the None singleton.

      +
      +
      +

      Inherited members

      + +
      class AsyncAttachingFunctionToken
      @@ -395,6 +471,12 @@

      Inherited members

    • Classes

      • +

        AsyncAttachingConversationKwargs

        + +
      • +
      • AsyncAttachingFunctionToken

      • diff --git a/docs/reference/middleware/async_middleware.html b/docs/reference/middleware/async_middleware.html index 33b4273e7..f7713b881 100644 --- a/docs/reference/middleware/async_middleware.html +++ b/docs/reference/middleware/async_middleware.html @@ -104,6 +104,7 @@

        Subclasses

        • AsyncAssistant
        • AsyncCustomMiddleware
        • +
        • AsyncAttachingConversationKwargs
        • AsyncAttachingFunctionToken
        • AsyncAuthorization
        • AsyncIgnoringSelfEvents
        • diff --git a/docs/reference/middleware/async_middleware_error_handler.html b/docs/reference/middleware/async_middleware_error_handler.html index e7cd8bb32..bf5b101f6 100644 --- a/docs/reference/middleware/async_middleware_error_handler.html +++ b/docs/reference/middleware/async_middleware_error_handler.html @@ -77,9 +77,10 @@

          Classes

          ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body
    • Ancestors

      diff --git a/docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html b/docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html new file mode 100644 index 000000000..a0f5bdf85 --- /dev/null +++ b/docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html @@ -0,0 +1,155 @@ + + + + + + +slack_bolt.middleware.attaching_conversation_kwargs.async_attaching_conversation_kwargs API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.middleware.attaching_conversation_kwargs.async_attaching_conversation_kwargs

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AsyncAttachingConversationKwargs +(thread_context_store: AsyncAssistantThreadContextStore | None = None) +
      +
      +
      + +Expand source code + +
      class AsyncAttachingConversationKwargs(AsyncMiddleware):
      +
      +    thread_context_store: Optional[AsyncAssistantThreadContextStore]
      +
      +    def __init__(self, thread_context_store: Optional[AsyncAssistantThreadContextStore] = None):
      +        self.thread_context_store = thread_context_store
      +
      +    async def async_process(
      +        self,
      +        *,
      +        req: AsyncBoltRequest,
      +        resp: BoltResponse,
      +        next: Callable[[], Awaitable[BoltResponse]],
      +    ) -> Optional[BoltResponse]:
      +        event = to_event(req.body)
      +        if event is not None:
      +            if is_assistant_event(req.body):
      +                assistant = AsyncAssistantUtilities(
      +                    payload=event,
      +                    context=req.context,
      +                    thread_context_store=self.thread_context_store,
      +                )
      +                req.context["say"] = assistant.say
      +                req.context["set_title"] = assistant.set_title
      +                req.context["set_suggested_prompts"] = assistant.set_suggested_prompts
      +                req.context["get_thread_context"] = assistant.get_thread_context
      +                req.context["save_thread_context"] = assistant.save_thread_context
      +
      +            # TODO: in the future we might want to introduce a "proper" extract_ts utility
      +            thread_ts = req.context.thread_ts or event.get("ts")
      +            if req.context.channel_id and thread_ts:
      +                req.context["set_status"] = AsyncSetStatus(
      +                    client=req.context.client,
      +                    channel_id=req.context.channel_id,
      +                    thread_ts=thread_ts,
      +                )
      +                req.context["say_stream"] = AsyncSayStream(
      +                    client=req.context.client,
      +                    channel=req.context.channel_id,
      +                    recipient_team_id=req.context.team_id or req.context.enterprise_id,
      +                    recipient_user_id=req.context.user_id,
      +                    thread_ts=thread_ts,
      +                )
      +        return await next()
      +
      +

      A middleware can process request data before other middleware and listener functions.

      +

      Ancestors

      + +

      Class variables

      +
      +
      var thread_context_storeAsyncAssistantThreadContextStore | None
      +
      +

      The type of the None singleton.

      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + diff --git a/docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html b/docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html new file mode 100644 index 000000000..8a1911323 --- /dev/null +++ b/docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html @@ -0,0 +1,149 @@ + + + + + + +slack_bolt.middleware.attaching_conversation_kwargs.attaching_conversation_kwargs API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.middleware.attaching_conversation_kwargs.attaching_conversation_kwargs

      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AttachingConversationKwargs +(thread_context_store: AssistantThreadContextStore | None = None) +
      +
      +
      + +Expand source code + +
      class AttachingConversationKwargs(Middleware):
      +
      +    thread_context_store: Optional[AssistantThreadContextStore]
      +
      +    def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = None):
      +        self.thread_context_store = thread_context_store
      +
      +    def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]:
      +        event = to_event(req.body)
      +        if event is not None:
      +            if is_assistant_event(req.body):
      +                assistant = AssistantUtilities(
      +                    payload=event,
      +                    context=req.context,
      +                    thread_context_store=self.thread_context_store,
      +                )
      +                req.context["say"] = assistant.say
      +                req.context["set_title"] = assistant.set_title
      +                req.context["set_suggested_prompts"] = assistant.set_suggested_prompts
      +                req.context["get_thread_context"] = assistant.get_thread_context
      +                req.context["save_thread_context"] = assistant.save_thread_context
      +
      +            # TODO: in the future we might want to introduce a "proper" extract_ts utility
      +            thread_ts = req.context.thread_ts or event.get("ts")
      +            if req.context.channel_id and thread_ts:
      +                req.context["set_status"] = SetStatus(
      +                    client=req.context.client,
      +                    channel_id=req.context.channel_id,
      +                    thread_ts=thread_ts,
      +                )
      +                req.context["say_stream"] = SayStream(
      +                    client=req.context.client,
      +                    channel=req.context.channel_id,
      +                    recipient_team_id=req.context.team_id or req.context.enterprise_id,
      +                    recipient_user_id=req.context.user_id,
      +                    thread_ts=thread_ts,
      +                )
      +        return next()
      +
      +

      A middleware can process request data before other middleware and listener functions.

      +

      Ancestors

      + +

      Class variables

      +
      +
      var thread_context_storeAssistantThreadContextStore | None
      +
      +

      The type of the None singleton.

      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + diff --git a/docs/reference/middleware/attaching_conversation_kwargs/index.html b/docs/reference/middleware/attaching_conversation_kwargs/index.html new file mode 100644 index 000000000..308a52712 --- /dev/null +++ b/docs/reference/middleware/attaching_conversation_kwargs/index.html @@ -0,0 +1,166 @@ + + + + + + +slack_bolt.middleware.attaching_conversation_kwargs API documentation + + + + + + + + + + + +
      +
      +
      +

      Module slack_bolt.middleware.attaching_conversation_kwargs

      +
      +
      +
      +
      +

      Sub-modules

      +
      +
      slack_bolt.middleware.attaching_conversation_kwargs.async_attaching_conversation_kwargs
      +
      +
      +
      +
      slack_bolt.middleware.attaching_conversation_kwargs.attaching_conversation_kwargs
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class AttachingConversationKwargs +(thread_context_store: AssistantThreadContextStore | None = None) +
      +
      +
      + +Expand source code + +
      class AttachingConversationKwargs(Middleware):
      +
      +    thread_context_store: Optional[AssistantThreadContextStore]
      +
      +    def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = None):
      +        self.thread_context_store = thread_context_store
      +
      +    def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]:
      +        event = to_event(req.body)
      +        if event is not None:
      +            if is_assistant_event(req.body):
      +                assistant = AssistantUtilities(
      +                    payload=event,
      +                    context=req.context,
      +                    thread_context_store=self.thread_context_store,
      +                )
      +                req.context["say"] = assistant.say
      +                req.context["set_title"] = assistant.set_title
      +                req.context["set_suggested_prompts"] = assistant.set_suggested_prompts
      +                req.context["get_thread_context"] = assistant.get_thread_context
      +                req.context["save_thread_context"] = assistant.save_thread_context
      +
      +            # TODO: in the future we might want to introduce a "proper" extract_ts utility
      +            thread_ts = req.context.thread_ts or event.get("ts")
      +            if req.context.channel_id and thread_ts:
      +                req.context["set_status"] = SetStatus(
      +                    client=req.context.client,
      +                    channel_id=req.context.channel_id,
      +                    thread_ts=thread_ts,
      +                )
      +                req.context["say_stream"] = SayStream(
      +                    client=req.context.client,
      +                    channel=req.context.channel_id,
      +                    recipient_team_id=req.context.team_id or req.context.enterprise_id,
      +                    recipient_user_id=req.context.user_id,
      +                    thread_ts=thread_ts,
      +                )
      +        return next()
      +
      +

      A middleware can process request data before other middleware and listener functions.

      +

      Ancestors

      + +

      Class variables

      +
      +
      var thread_context_storeAssistantThreadContextStore | None
      +
      +

      The type of the None singleton.

      +
      +
      +

      Inherited members

      + +
      +
      +
      +
      + +
      + + + diff --git a/docs/reference/middleware/index.html b/docs/reference/middleware/index.html index 05d773415..ce2629224 100644 --- a/docs/reference/middleware/index.html +++ b/docs/reference/middleware/index.html @@ -65,6 +65,10 @@

      Sub-modules

      +
      slack_bolt.middleware.attaching_conversation_kwargs
      +
      +
      +
      slack_bolt.middleware.attaching_function_token
      @@ -114,6 +118,76 @@

      Sub-modules

      Classes

      +
      +class AttachingConversationKwargs +(thread_context_store: AssistantThreadContextStore | None = None) +
      +
      +
      + +Expand source code + +
      class AttachingConversationKwargs(Middleware):
      +
      +    thread_context_store: Optional[AssistantThreadContextStore]
      +
      +    def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = None):
      +        self.thread_context_store = thread_context_store
      +
      +    def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]:
      +        event = to_event(req.body)
      +        if event is not None:
      +            if is_assistant_event(req.body):
      +                assistant = AssistantUtilities(
      +                    payload=event,
      +                    context=req.context,
      +                    thread_context_store=self.thread_context_store,
      +                )
      +                req.context["say"] = assistant.say
      +                req.context["set_title"] = assistant.set_title
      +                req.context["set_suggested_prompts"] = assistant.set_suggested_prompts
      +                req.context["get_thread_context"] = assistant.get_thread_context
      +                req.context["save_thread_context"] = assistant.save_thread_context
      +
      +            # TODO: in the future we might want to introduce a "proper" extract_ts utility
      +            thread_ts = req.context.thread_ts or event.get("ts")
      +            if req.context.channel_id and thread_ts:
      +                req.context["set_status"] = SetStatus(
      +                    client=req.context.client,
      +                    channel_id=req.context.channel_id,
      +                    thread_ts=thread_ts,
      +                )
      +                req.context["say_stream"] = SayStream(
      +                    client=req.context.client,
      +                    channel=req.context.channel_id,
      +                    recipient_team_id=req.context.team_id or req.context.enterprise_id,
      +                    recipient_user_id=req.context.user_id,
      +                    thread_ts=thread_ts,
      +                )
      +        return next()
      +
      +

      A middleware can process request data before other middleware and listener functions.

      +

      Ancestors

      + +

      Class variables

      +
      +
      var thread_context_storeAssistantThreadContextStore | None
      +
      +

      The type of the None singleton.

      +
      +
      +

      Inherited members

      + +
      class AttachingFunctionToken
      @@ -385,6 +459,7 @@

      Inherited members

      Subclasses

      Ancestors

      diff --git a/docs/reference/oauth/async_callback_options.html b/docs/reference/oauth/async_callback_options.html index 822867ea8..d07f1aee5 100644 --- a/docs/reference/oauth/async_callback_options.html +++ b/docs/reference/oauth/async_callback_options.html @@ -101,7 +101,7 @@

      Class variables

      reason: str, error: Optional[Exception] = None, suggested_status_code: int, - settings: "AsyncOAuthSettings", # type: ignore[name-defined] + settings: "AsyncOAuthSettings", default: "AsyncCallbackOptions", ): """The arguments for a failure function. @@ -153,7 +153,7 @@

      Args

      *, request: AsyncBoltRequest, installation: Installation, - settings: "AsyncOAuthSettings", # type: ignore[name-defined] + settings: "AsyncOAuthSettings", default: "AsyncCallbackOptions", ): """The arguments for a success function. diff --git a/docs/reference/oauth/async_oauth_settings.html b/docs/reference/oauth/async_oauth_settings.html index 3b8c04edb..5e6a543c4 100644 --- a/docs/reference/oauth/async_oauth_settings.html +++ b/docs/reference/oauth/async_oauth_settings.html @@ -48,7 +48,7 @@

      Classes

      class AsyncOAuthSettings -(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: AsyncCallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.async_oauth_settings (WARNING)>)
      +(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: AsyncCallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.async_oauth_settings (WARNING)>)
      diff --git a/docs/reference/oauth/callback_options.html b/docs/reference/oauth/callback_options.html index 7ad3734b3..c6fc81286 100644 --- a/docs/reference/oauth/callback_options.html +++ b/docs/reference/oauth/callback_options.html @@ -181,7 +181,7 @@

      Inherited members

      reason: str, error: Optional[Exception] = None, suggested_status_code: int, - settings: "OAuthSettings", # type: ignore[name-defined] + settings: "OAuthSettings", default: "CallbackOptions", ): """The arguments for a failure function. @@ -233,7 +233,7 @@

      Args

      *, request: BoltRequest, installation: Installation, - settings: "OAuthSettings", # type: ignore[name-defined] + settings: "OAuthSettings", default: "CallbackOptions", ): """The arguments for a success function. diff --git a/docs/reference/oauth/oauth_settings.html b/docs/reference/oauth/oauth_settings.html index cd8def497..1eb2ab7dd 100644 --- a/docs/reference/oauth/oauth_settings.html +++ b/docs/reference/oauth/oauth_settings.html @@ -48,7 +48,7 @@

      Classes

      class OAuthSettings -(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: CallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.state_store.OAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.oauth_settings (WARNING)>)
      +(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: CallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.state_store.OAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.oauth_settings (WARNING)>)
      diff --git a/docs/reference/request/internals.html b/docs/reference/request/internals.html index bc13932ec..bd8319183 100644 --- a/docs/reference/request/internals.html +++ b/docs/reference/request/internals.html @@ -268,12 +268,12 @@

      Functions

      return channel.get("id") if "channel_id" in payload: return payload.get("channel_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_channel_id(payload["event"]) - if payload.get("item") is not None: + if isinstance(payload.get("item"), dict): # reaction_added: body["event"]["item"] return extract_channel_id(payload["item"]) - if payload.get("assistant_thread") is not None: + if isinstance(payload.get("assistant_thread"), dict): # assistant_thread_started return extract_channel_id(payload["assistant_thread"]) return None @@ -317,10 +317,10 @@

      Functions

      return extract_enterprise_id(payload["authorizations"][0]) if "enterprise_id" in payload: return payload.get("enterprise_id") - if payload.get("team") is not None and "enterprise_id" in payload["team"]: + if isinstance(payload.get("team"), dict) and "enterprise_id" in payload["team"]: # In the case where the type is view_submission return payload["team"].get("enterprise_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_enterprise_id(payload["event"]) return None
      @@ -337,7 +337,7 @@

      Functions

      def extract_function_bot_access_token(payload: Dict[str, Any]) -> Optional[str]:
           if payload.get("bot_access_token") is not None:
               return payload.get("bot_access_token")
      -    if payload.get("event") is not None:
      +    if isinstance(payload.get("event"), dict):
               return payload["event"].get("bot_access_token")
           return None
      @@ -354,9 +354,9 @@

      Functions

      def extract_function_execution_id(payload: Dict[str, Any]) -> Optional[str]:
           if payload.get("function_execution_id") is not None:
               return payload.get("function_execution_id")
      -    if payload.get("event") is not None:
      +    if isinstance(payload.get("event"), dict):
               return extract_function_execution_id(payload["event"])
      -    if payload.get("function_data") is not None:
      +    if isinstance(payload.get("function_data"), dict):
               return payload["function_data"].get("execution_id")
           return None
      @@ -371,9 +371,9 @@

      Functions

      Expand source code
      def extract_function_inputs(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
      -    if payload.get("event") is not None:
      +    if isinstance(payload.get("event"), dict):
               return payload["event"].get("inputs")
      -    if payload.get("function_data") is not None:
      +    if isinstance(payload.get("function_data"), dict):
               return payload["function_data"].get("inputs")
           return None
      @@ -408,13 +408,13 @@

      Functions

      Expand source code
      def extract_team_id(payload: Dict[str, Any]) -> Optional[str]:
      -    app_installed_team_id = payload.get("view", {}).get("app_installed_team_id")
      -    if app_installed_team_id is not None:
      +    view = payload.get("view")
      +    if isinstance(view, dict) and view.get("app_installed_team_id") is not None:
               # view_submission payloads can have `view.app_installed_team_id` when a modal view that was opened
               # in a different workspace via some operations inside a Slack Connect channel.
               # Note that the same for enterprise_id does not exist. When you need to know the enterprise_id as well,
               # you have to run some query toward your InstallationStore to know the org where the team_id belongs to.
      -        return app_installed_team_id
      +        return view["app_installed_team_id"]
           if payload.get("team") is not None:
               # With org-wide installations, payload.team in interactivity payloads can be None
               # You need to extract either payload.user.team_id or payload.view.team_id as below
      @@ -429,12 +429,12 @@ 

      Functions

      return extract_team_id(payload["authorizations"][0]) if "team_id" in payload: return payload.get("team_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_team_id(payload["event"]) - if payload.get("user") is not None: + if isinstance(payload.get("user"), dict): return payload["user"]["team_id"] - if payload.get("view") is not None: - return payload.get("view", {})["team_id"] + if isinstance(payload.get("view"), dict): + return payload["view"]["team_id"] return None
      @@ -448,30 +448,17 @@

      Functions

      Expand source code
      def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]:
      -    # This utility initially supports only the use cases for AI assistants, but it may be fine to add more patterns.
      -    # That said, note that thread_ts is always required for assistant threads, but it's not for channels.
      -    # Thus, blindly setting this thread_ts to say utility can break existing apps' behaviors.
      -    if is_assistant_event(payload):
      -        event = payload["event"]
      -        if (
      -            event.get("assistant_thread") is not None
      -            and event["assistant_thread"].get("channel_id") is not None
      -            and event["assistant_thread"].get("thread_ts") is not None
      -        ):
      -            # assistant_thread_started, assistant_thread_context_changed
      -            # "assistant_thread" property can exist for message event without channel_id and thread_ts
      -            # Thus, the above if check verifies these properties exist
      -            return event["assistant_thread"]["thread_ts"]
      -        elif event.get("channel") is not None:
      -            if event.get("thread_ts") is not None:
      -                # message in an assistant thread
      -                return event["thread_ts"]
      -            elif event.get("message", {}).get("thread_ts") is not None:
      -                # message_changed
      -                return event["message"]["thread_ts"]
      -            elif event.get("previous_message", {}).get("thread_ts") is not None:
      -                # message_deleted
      -                return event["previous_message"]["thread_ts"]
      +    thread_ts = payload.get("thread_ts")
      +    if thread_ts is not None:
      +        return thread_ts
      +    if isinstance(payload.get("event"), dict):
      +        return extract_thread_ts(payload["event"])
      +    if isinstance(payload.get("assistant_thread"), dict):
      +        return extract_thread_ts(payload["assistant_thread"])
      +    if isinstance(payload.get("message"), dict):
      +        return extract_thread_ts(payload["message"])
      +    if isinstance(payload.get("previous_message"), dict):
      +        return extract_thread_ts(payload["previous_message"])
           return None
      @@ -493,12 +480,12 @@

      Functions

      return user.get("id") if "user_id" in payload: return payload.get("user_id") - if payload.get("event") is not None: + if isinstance(payload.get("event"), dict): return extract_user_id(payload["event"]) - if payload.get("message") is not None: + if isinstance(payload.get("message"), dict): # message_changed: body["event"]["message"] return extract_user_id(payload["message"]) - if payload.get("previous_message") is not None: + if isinstance(payload.get("previous_message"), dict): # message_deleted: body["event"]["previous_message"] return extract_user_id(payload["previous_message"]) return None diff --git a/slack_bolt/version.py b/slack_bolt/version.py index 9b1349aea..ebda7dafb 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.27.0" +__version__ = "1.28.0" From 7e9b08bf636c7b2193a2fe5da277a5e7f2c5fd8f Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:23:22 -0700 Subject: [PATCH 214/282] docs: agent kit (#1478) Co-authored-by: William Bergamin Co-authored-by: Tracy Rericha <108959677+technically-tracy@users.noreply.github.com> Co-authored-by: William Bergamin --- docs/english/_sidebar.json | 21 +- .../english/concepts/adding-agent-features.md | 746 ++++++++++++++++++ docs/english/concepts/message-sending.md | 75 +- ...i-apps.md => using-the-assistant-class.md} | 201 +---- ...{building-an-app.md => creating-an-app.md} | 6 +- docs/english/experiments.md | 4 - docs/english/getting-started.md | 51 +- .../english/tutorial/ai-chatbot/ai-chatbot.md | 134 ++-- docs/japanese/concepts/assistant.md | 227 ------ 9 files changed, 892 insertions(+), 573 deletions(-) create mode 100644 docs/english/concepts/adding-agent-features.md rename docs/english/concepts/{ai-apps.md => using-the-assistant-class.md} (66%) rename docs/english/{building-an-app.md => creating-an-app.md} (99%) delete mode 100644 docs/japanese/concepts/assistant.md diff --git a/docs/english/_sidebar.json b/docs/english/_sidebar.json index eab9d94f8..79721bdcd 100644 --- a/docs/english/_sidebar.json +++ b/docs/english/_sidebar.json @@ -7,7 +7,19 @@ }, "tools/bolt-python/getting-started", { "type": "html", "value": "
      " }, - "tools/bolt-python/building-an-app", + "tools/bolt-python/creating-an-app", + { + "type": "category", + "label": "AI & Agents", + "link": { + "type": "doc", + "id": "tools/bolt-python/concepts/adding-agent-features" + }, + "items": [ + "tools/bolt-python/concepts/adding-agent-features", + "tools/bolt-python/concepts/using-the-assistant-class" + ] + }, { "type": "category", "label": "Slack API calls", @@ -39,7 +51,6 @@ "tools/bolt-python/concepts/app-home" ] }, - "tools/bolt-python/concepts/ai-apps", { "type": "category", "label": "Custom Steps", @@ -85,11 +96,7 @@ "tools/bolt-python/concepts/token-rotation" ] }, - { - "type": "category", - "label": "Experiments", - "items": ["tools/bolt-python/experiments"] - }, + "tools/bolt-python/experiments", { "type": "category", "label": "Legacy", diff --git a/docs/english/concepts/adding-agent-features.md b/docs/english/concepts/adding-agent-features.md new file mode 100644 index 000000000..cbd164630 --- /dev/null +++ b/docs/english/concepts/adding-agent-features.md @@ -0,0 +1,746 @@ +--- +sidebar_label: Adding agent features +--- + +# Adding agent features with Bolt for Python + +:::tip[Check out the Support Agent sample app] +The code snippets throughout this guide are from our [Support Agent sample app](https://github.com/slack-samples/bolt-python-support-agent), Casey, which supports integration with Pydantic, Anthropic, and OpenAI. + +View our [agent quickstart](/ai/agent-quickstart) to get up and running with Casey. Otherwise, read on for exploration and explanation of agent-focused Bolt features found within Casey. +::: + +Your agent can utilize features applicable to messages throughout Slack, like [chat streaming](#text-streaming) and [feedback buttons](#adding-and-handling-feedback). They can also [utilize the `Assistant` class](/tools/bolt-python/concepts/assistant-class) for a side-panel view designed with AI in mind. + +If you're unfamiliar with using these feature within Slack, you may want to read the [API docs on the subject](/ai/). Then come back here to implement them with Bolt! + +--- + +## Slack MCP Server {#slack-mcp-server} + +Casey can harness the [Slack MCP Server](https://docs.slack.dev/ai/slack-mcp-server/developing) when deployed via an HTTP Server with OAuth. + +To enable the Slack MCP Server: + +1. Install [ngrok](https://ngrok.com/download) and start a tunnel: + +```sh +ngrok http 3000 +``` + +2. Copy the `https://*.ngrok-free.app` URL from the ngrok output. + +3. Update `manifest.json` for HTTP mode: + - Set `socket_mode_enabled` to `false` + - Replace `ngrok-free.app` with your ngrok domain (e.g. `YOUR_NGROK_SUBDOMAIN.ngrok-free.app`) + +4. Create a new local dev app: + +```sh +slack install -E local +``` + +5. Enable MCP for your app: + - Run `slack app settings` to open your app's settings + - Navigate to **Agents & AI Apps** in the left-side navigation + - Toggle **Model Context Protocol** on + +6. Update your `.env` OAuth environment variables: + - Run `slack app settings` to open App Settings + - Copy **Client ID**, **Client Secret**, and **Signing Secret** + - Update `SLACK_REDIRECT_URI` in `.env` with your ngrok domain + +```sh +SLACK_CLIENT_ID=YOUR_CLIENT_ID +SLACK_CLIENT_SECRET=YOUR_CLIENT_SECRET +SLACK_REDIRECT_URI=https://YOUR_NGROK_SUBDOMAIN.ngrok-free.app/slack/oauth_redirect +SLACK_SIGNING_SECRET=YOUR_SIGNING_SECRET +``` + +7. Start the app: + +```sh +slack run app_oauth.py +``` + +8. Click the install URL printed in the terminal to install the app to your workspace via OAuth. + +Your agent can now access the Slack MCP server! + +--- + +## Listening for user invocation + +Agents can be invoked throughout Slack, such as via @mentions in channels, messaging the agent, and using the assistant side panel. + + + + +```python +import re +from logging import Logger + +from agents import Runner +from slack_bolt import BoltContext, Say, SayStream, SetStatus +from slack_sdk import WebClient + +from agent import CaseyDeps, casey_agent +from thread_context import conversation_store +from listeners.views.feedback_builder import build_feedback_blocks + + +def handle_app_mentioned( + client: WebClient, + context: BoltContext, + event: dict, + logger: Logger, + say: Say, + say_stream: SayStream, + set_status: SetStatus, +): + """Handle @Casey mentions in channels.""" + try: + channel_id = context.channel_id + text = event.get("text", "") + thread_ts = event.get("thread_ts") or event["ts"] + user_id = context.user_id + + # Strip the bot mention from the text + cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() + + if not cleaned_text: + say( + text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", + thread_ts=thread_ts, + ) + return + + # Add eyes reaction only to the first message (not threaded replies) + if not event.get("thread_ts"): + client.reactions_add( + channel=channel_id, + timestamp=event["ts"], + name="eyes", + ) + ... +``` + + + + +```python +from logging import Logger + +from slack_bolt.context import BoltContext +from slack_bolt.context.say import Say +from slack_bolt.context.say_stream import SayStream +from slack_bolt.context.set_status import SetStatus +from slack_sdk import WebClient + +from agent import CaseyDeps, run_casey_agent +from thread_context import session_store +from listeners.views.feedback_builder import build_feedback_blocks + + +def handle_message( + client: WebClient, + context: BoltContext, + event: dict, + logger: Logger, + say: Say, + say_stream: SayStream, + set_status: SetStatus, +): + """Handle messages sent to Casey via DM or in threads the bot is part of.""" + # Issue submissions are posted by the bot with metadata so the message + # handler can run the agent on behalf of the original user. + is_issue_submission = ( + event.get("metadata", {}).get("event_type") == "issue_submission" + ) + + # Skip message subtypes (edits, deletes, etc.) and bot messages that + # are not issue submissions. + if event.get("subtype"): + return + if event.get("bot_id") and not is_issue_submission: + return + + is_dm = event.get("channel_type") == "im" + is_thread_reply = event.get("thread_ts") is not None + + if is_dm: + pass + elif is_thread_reply: + # Channel thread replies are handled only if the bot is already engaged + session = session_store.get_session(context.channel_id, event["thread_ts"]) + if session is None: + return + else: + # Top-level channel messages are handled by app_mentioned + return + + try: + channel_id = context.channel_id + text = event.get("text", "") + thread_ts = event.get("thread_ts") or event["ts"] + + # Get session ID for conversation context + existing_session_id = session_store.get_session(channel_id, thread_ts) + + # Add eyes reaction only to the first message (DMs only — channel + # threads already have the reaction from the initial app_mention) + if is_dm and not existing_session_id: + await client.reactions_add( + channel=channel_id, + timestamp=event["ts"], + name="eyes", + ) + + ... +``` + + + + + +:::tip[Using the Assistant side panel] +The Assistant side panel requires additional setup. See the [Assistant class guide](/tools/bolt-python/concepts/assistant-class). +::: + + +```py +from logging import Logger + +from slack_bolt.context.set_suggested_prompts import SetSuggestedPrompts + +SUGGESTED_PROMPTS = [ + {"title": "Reset Password", "message": "I need to reset my password"}, + {"title": "Request Access", "message": "I need access to a system or tool"}, + {"title": "Network Issues", "message": "I'm having network connectivity issues"}, +] + + +def handle_assistant_thread_started( + set_suggested_prompts: SetSuggestedPrompts, logger: Logger +): + """Handle assistant thread started events by setting suggested prompts.""" + try: + set_suggested_prompts( + prompts=SUGGESTED_PROMPTS, + title="How can I help you today?", + ) + except Exception as e: + logger.exception(f"Failed to handle assistant thread started: {e}") +``` + + + + +--- + +## Setting status {#setting-assistant-status} + +Your app can show actions are happening behind the scenes by setting its thread status. + +```python +def handle_app_mentioned( + set_status: SetStatus, + ... +): + set_status( + status="Thinking...", + loading_messages=[ + "Teaching the hamsters to type faster…", + "Untangling the internet cables…", + "Consulting the office goldfish…", + "Polishing up the response just for you…", + "Convincing the AI to stop overthinking…", + ], + ) +``` + +--- + +## Streaming messages {#text-streaming} + +You can have your app's messages stream in to replicate conventional agent behavior. Bolt for Python provides a `say_stream` utility as a listener argument available for `app.event` and `app.message` listeners. + +The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient.chat_stream`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility by sourcing parameter values from the relevant event payload. + +| Parameter | Value | +|---|---| +| `channel_id` | Sourced from the event payload. +| `thread_ts` | Sourced from the event payload. Falls back to the `ts` value if available. +| `recipient_team_id` | Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). +| `recipient_user_id` | Sourced from the `user_id` of the event. + +If neither a `channel_id` or `thread_ts` can be sourced, then the utility will be `None`. + +```python +streamer = say_stream() +streamer.append(markdown_text="Here's my response...") +streamer.append(markdown_text="And here's more...") +streamer.stop() +``` + +--- + +## Adding and handling feedback {#adding-and-handling-feedback} + +You can use the [feedback buttons block element](/reference/block-kit/block-elements/feedback-buttons-element/) to allow users to immediately provide feedback regarding the app's responses. Here's what the feedback buttons look like from the Support Agent sample app: + +```py title=".../listeners/views/feedback_builder.py" +from slack_sdk.models.blocks import ( + Block, + ContextActionsBlock, + FeedbackButtonObject, + FeedbackButtonsElement, +) + + +def build_feedback_blocks() -> list[Block]: + """Build feedback blocks with thumbs up/down buttons.""" + return [ + ContextActionsBlock( + elements=[ + FeedbackButtonsElement( + action_id="feedback", + positive_button=FeedbackButtonObject( + text="Good Response", + accessibility_label="Submit positive feedback on this response", + value="good-feedback", + ), + negative_button=FeedbackButtonObject( + text="Bad Response", + accessibility_label="Submit negative feedback on this response", + value="bad-feedback", + ), + ) + ] + ) + ] +``` + +That feedback block is then rendered at the bottom of your app's message via the `say_stream` utility. + +```py +... + # Stream response in thread with feedback buttons + streamer = say_stream() + streamer.append(markdown_text=result.output) + feedback_blocks = build_feedback_blocks() + streamer.stop(blocks=feedback_blocks) +... +``` + +You can also add a response for when the user provides feedback. + +```python title="...listeners/actions/feedback_button.py" +from logging import Logger + +from slack_bolt import Ack, BoltContext +from slack_sdk import WebClient + + +def handle_feedback_button( + ack: Ack, body: dict, client: WebClient, context: BoltContext, logger: Logger +): + """Handle thumbs up/down feedback on Casey's responses.""" + ack() + + try: + channel_id = context.channel_id + user_id = context.user_id + message_ts = body["message"]["ts"] + feedback_value = body["actions"][0]["value"] + + if feedback_value == "good-feedback": + client.chat_postEphemeral( + channel=channel_id, + user=user_id, + thread_ts=message_ts, + text="Glad that was helpful! :tada:", + ) + else: + client.chat_postEphemeral( + channel=channel_id, + user=user_id, + thread_ts=message_ts, + text="Sorry that wasn't helpful. :slightly_frowning_face: Try rephrasing your question or I can create a support ticket for you.", + ) + + logger.debug( + f"Feedback received: value={feedback_value}, message_ts={message_ts}" + ) + except Exception as e: + logger.exception(f"Failed to handle feedback: {e}") +``` + +--- + +## Full example + +Putting all those concepts together results in a dynamic agent ready to helpfully respond. + + +
      +Full example + + + +```python title="app_mentioned.py" +import re +from logging import Logger + +from slack_bolt import BoltContext, Say, SayStream, SetStatus +from slack_sdk import WebClient + +from agent import CaseyDeps, casey_agent, get_model +from thread_context import conversation_store +from listeners.views.feedback_builder import build_feedback_blocks + + +def handle_app_mentioned( + client: WebClient, + context: BoltContext, + event: dict, + logger: Logger, + say: Say, + say_stream: SayStream, + set_status: SetStatus, +): + """Handle @Casey mentions in channels.""" + try: + channel_id = context.channel_id + text = event.get("text", "") + thread_ts = event.get("thread_ts") or event["ts"] + user_id = context.user_id + + # Strip the bot mention from the text + cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() + + if not cleaned_text: + say( + text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", + thread_ts=thread_ts, + ) + return + + # Add eyes reaction only to the first message (not threaded replies) + if not event.get("thread_ts"): + client.reactions_add( + channel=channel_id, + timestamp=event["ts"], + name="eyes", + ) + + # Set assistant thread status with loading messages + set_status( + status="Thinking...", + loading_messages=[ + "Teaching the hamsters to type faster…", + "Untangling the internet cables…", + "Consulting the office goldfish…", + "Polishing up the response just for you…", + "Convincing the AI to stop overthinking…", + ], + ) + + # Get conversation history + history = conversation_store.get_history(channel_id, thread_ts) + + # Run the agent + deps = CaseyDeps( + client=client, + user_id=user_id, + channel_id=channel_id, + thread_ts=thread_ts, + message_ts=event["ts"], + ) + result = casey_agent.run_sync( + cleaned_text, + model=get_model(), + deps=deps, + message_history=history, + ) + + # Stream response in thread with feedback buttons + streamer = say_stream() + streamer.append(markdown_text=result.output) + feedback_blocks = build_feedback_blocks() + streamer.stop(blocks=feedback_blocks) + + # Store conversation history + conversation_store.set_history(channel_id, thread_ts, result.all_messages()) + + except Exception as e: + logger.exception(f"Failed to handle app mention: {e}") + say( + text=f":warning: Something went wrong! ({e})", + thread_ts=event.get("thread_ts") or event["ts"], + ) +``` + + + + +```python title="app_mentioned.py" +import re +from logging import Logger + +from slack_bolt.context import BoltContext +from slack_bolt.context.say import Say +from slack_bolt.context.say_stream import SayStream +from slack_bolt.context.set_status import SetStatus +from slack_sdk import WebClient + +from agent import CaseyDeps, run_casey_agent +from thread_context import session_store +from listeners.views.feedback_builder import build_feedback_blocks + + +def handle_app_mentioned( + client: WebClient, + context: BoltContext, + event: dict, + logger: Logger, + say: Say, + say_stream: SayStream, + set_status: SetStatus, +): + """Handle @Casey mentions in channels.""" + try: + channel_id = context.channel_id + text = event.get("text", "") + thread_ts = event.get("thread_ts") or event["ts"] + + # Strip the bot mention from the text + cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() + + if not cleaned_text: + say( + text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", + thread_ts=thread_ts, + ) + return + + # Add eyes reaction only to the first message (not threaded replies) + if not event.get("thread_ts"): + client.reactions_add( + channel=channel_id, + timestamp=event["ts"], + name="eyes", + ) + + # Set assistant thread status with loading messages + set_status( + status="Thinking...", + loading_messages=[ + "Teaching the hamsters to type faster…", + "Untangling the internet cables…", + "Consulting the office goldfish…", + "Polishing up the response just for you…", + "Convincing the AI to stop overthinking…", + ], + ) + + # Get session ID for conversation context + existing_session_id = session_store.get_session(channel_id, thread_ts) + + # Run the agent with deps for tool access + deps = CaseyDeps( + client=client, + user_id=context.user_id, + channel_id=channel_id, + thread_ts=thread_ts, + message_ts=event["ts"], + ) + response_text, new_session_id = run_casey_agent( + cleaned_text, session_id=existing_session_id, deps=deps + ) + + # Stream response in thread with feedback buttons + streamer = say_stream() + streamer.append(markdown_text=response_text) + feedback_blocks = build_feedback_blocks() + streamer.stop(blocks=feedback_blocks) + + # Store session ID for future context + if new_session_id: + session_store.set_session(channel_id, thread_ts, new_session_id) + + except Exception as e: + logger.exception(f"Failed to handle app mention: {e}") + await say( + text=f":warning: Something went wrong! ({e})", + thread_ts=event.get("thread_ts") or event["ts"], + ) +``` + + + +```python title="app_mentioned.py" +import re +from logging import Logger + +from agents import Runner +from slack_bolt import BoltContext, Say, SayStream, SetStatus +from slack_sdk import WebClient + +from agent import CaseyDeps, casey_agent +from thread_context import conversation_store +from listeners.views.feedback_builder import build_feedback_blocks + + +def handle_app_mentioned( + client: WebClient, + context: BoltContext, + event: dict, + logger: Logger, + say: Say, + say_stream: SayStream, + set_status: SetStatus, +): + """Handle @Casey mentions in channels.""" + try: + channel_id = context.channel_id + text = event.get("text", "") + thread_ts = event.get("thread_ts") or event["ts"] + user_id = context.user_id + + # Strip the bot mention from the text + cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() + + if not cleaned_text: + say( + text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", + thread_ts=thread_ts, + ) + return + + # Add eyes reaction only to the first message (not threaded replies) + if not event.get("thread_ts"): + client.reactions_add( + channel=channel_id, + timestamp=event["ts"], + name="eyes", + ) + + # Set assistant thread status with loading messages + set_status( + status="Thinking...", + loading_messages=[ + "Teaching the hamsters to type faster…", + "Untangling the internet cables…", + "Consulting the office goldfish…", + "Polishing up the response just for you…", + "Convincing the AI to stop overthinking…", + ], + ) + + # Get conversation history + history = conversation_store.get_history(channel_id, thread_ts) + + # Build input for the agent + if history: + input_items = history + [{"role": "user", "content": cleaned_text}] + else: + input_items = cleaned_text + + # Run the agent + deps = CaseyDeps( + client=client, + user_id=user_id, + channel_id=channel_id, + thread_ts=thread_ts, + message_ts=event["ts"], + ) + result = Runner.run_sync(casey_agent, input=input_items, context=deps) + + # Stream response in thread with feedback buttons + streamer = say_stream() + streamer.append(markdown_text=result.final_output) + feedback_blocks = build_feedback_blocks() + streamer.stop(blocks=feedback_blocks) + + # Store conversation history + conversation_store.set_history(channel_id, thread_ts, result.to_input_list()) + + except Exception as e: + logger.exception(f"Failed to handle app mention: {e}") + say( + text=f":warning: Something went wrong! ({e})", + thread_ts=event.get("thread_ts") or event["ts"], + ) +``` + + + +
      + +--- + +## Onward: adding custom tools + +Casey comes with test tools and simulated systems. You can extend it with custom tools to make it a fully functioning Slack agent. + +In this example, we'll add a tool that makes live calls to check the GitHub status. + +1. Create `agent/tools/{tool-name}.py` and define the tool with the `@tool` decorator: + +```python title="agent/tools/check_github_status.py" +from claude_agent_sdk import tool +import httpx + +@tool( + name="check_github_status", + description="Check GitHub's current operational status", + input_schema={}, +) +async def check_github_status_tool(args): + """Check if GitHub is operational.""" + async with httpx.AsyncClient() as client: + response = await client.get("https://www.githubstatus.com/api/v2/status.json") + data = response.json() + status = data["status"]["indicator"] + description = data["status"]["description"] + + return { + "content": [ + { + "type": "text", + "text": f"**GitHub Status** — {status}\n{description}", + } + ] + } +``` + +2. Import the tool in `agent/casey.py`: + +```python title="agent/casey.py" +from agent.tools import check_github_status_tool +``` + +3. Register in `casey_tools_server`: + +```python title="agent/casey.py" +casey_tools_server = create_sdk_mcp_server( + name="casey-tools", + version="1.0.0", + tools=[ + check_github_status_tool, # Add here + # ... other tools + ], +) +``` + +4. Add to `CASEY_TOOLS`: + +```python title="agent/casey.py" +CASEY_TOOLS = [ + "check_github_status", # Add here + # ... other tools +] +``` + +Use this example as a jumping off point for building out an agent with the capabilities you need! \ No newline at end of file diff --git a/docs/english/concepts/message-sending.md b/docs/english/concepts/message-sending.md index 87c433129..090503ff2 100644 --- a/docs/english/concepts/message-sending.md +++ b/docs/english/concepts/message-sending.md @@ -43,37 +43,58 @@ def show_datepicker(event, say): ## Streaming messages {#streaming-messages} -You can have your app's messages stream in to replicate conventional AI chatbot behavior. This is done through three Web API methods: +You can have your app's messages stream in to replicate conventional agent behavior. Bolt for Python provides a `say_stream` utility as a listener argument available for `app.event` and `app.message` listeners. -* [`chat_startStream`](/reference/methods/chat.startStream) -* [`chat_appendStream`](/reference/methods/chat.appendStream) -* [`chat_stopStream`](/reference/methods/chat.stopStream) +The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient.chat_stream`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility by sourcing parameter values from the relevant event payload. -The Python Slack SDK provides a [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility to streamline calling these methods. Here's an excerpt from our [Assistant template app](https://github.com/slack-samples/bolt-python-assistant-template): +| Parameter | Value | +|---|---| +| `channel_id` | Sourced from the event payload. +| `thread_ts` | Sourced from the event payload. Falls back to the `ts` value if available. +| `recipient_team_id` | Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). +| `recipient_user_id` | Sourced from the `user_id` of the event. -```python -streamer = client.chat_stream( - channel=channel_id, - recipient_team_id=team_id, - recipient_user_id=user_id, - thread_ts=thread_ts, -) - -# Loop over OpenAI response stream -# https://platform.openai.com/docs/api-reference/responses/create -for event in returned_message: - if event.type == "response.output_text.delta": - streamer.append(markdown_text=f"{event.delta}") - else: - continue - -feedback_block = create_feedback_block() -streamer.stop(blocks=feedback_block) +If neither a `channel_id` or `thread_ts` can be sourced, then the utility will be `None`. + +For information on calling the `chat_*Stream` API methods directly, see the [_Sending streaming messages_](/tools/python-slack-sdk/web#sending-streaming-messages) section of the Python Slack SDK docs. + +### Example {#example} + +```py +import os + +from slack_bolt import App, SayStream +from slack_bolt.adapter.socket_mode import SocketModeHandler +from slack_sdk import WebClient + +app = App(token=os.environ.get("SLACK_BOT_TOKEN")) + +@app.event("app_mention") +def handle_app_mention(client: WebClient, say_stream: SayStream): + stream = say_stream() + stream.append(markdown_text="Someone rang the bat signal!") + stream.stop() + +@app.message("") +def handle_message(client: WebClient, say_stream: SayStream): + stream = say_stream() + + stream.append(markdown_text="Let me consult my *vast knowledge database*...) + stream.stop() + +if __name__ == "__main__": + SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start() ``` -In that example, a [feedback buttons](/reference/block-kit/block-elements/feedback-buttons-element) block element is passed to `streamer.stop` to provide feedback buttons to the user at the bottom of the message. Interaction with these buttons will send a block action event to your app to receive the feedback. +#### Adding feedback buttons after a stream -```python +You can pass a [feedback buttons](/reference/block-kit/block-elements/feedback-buttons-element) block element to `stream.stop` to provide feedback buttons to the user at the bottom of the message. Interaction with these buttons will send a block action event to your app to receive the feedback. + +```py +stream.stop(blocks=feedback_block) +``` + +```py def create_feedback_block() -> List[Block]: blocks: List[Block] = [ ContextActionsBlock( @@ -95,6 +116,4 @@ def create_feedback_block() -> List[Block]: ) ] return blocks -``` - -For information on calling the `chat_*Stream` API methods without the helper utility, see the [_Sending streaming messages_](/tools/python-slack-sdk/web#sending-streaming-messages) section of the Python Slack SDK docs. \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/english/concepts/ai-apps.md b/docs/english/concepts/using-the-assistant-class.md similarity index 66% rename from docs/english/concepts/ai-apps.md rename to docs/english/concepts/using-the-assistant-class.md index 3b057bc7e..ed004dc35 100644 --- a/docs/english/concepts/ai-apps.md +++ b/docs/english/concepts/using-the-assistant-class.md @@ -1,17 +1,10 @@ - -# Using AI in Apps {#using-ai-in-apps} - -The Slack platform offers features tailored for AI agents and assistants. Your apps can [utilize the `Assistant` class](#assistant) for a side-panel view designed with AI in mind, or they can utilize features applicable to messages throughout Slack, like [chat streaming](#text-streaming) and [feedback buttons](#adding-and-handling-feedback). - -If you're unfamiliar with using these feature within Slack, you may want to read the [API documentation on the subject](/ai/). Then come back here to implement them with Bolt! - -## The `Assistant` class instance {#assistant} +# Using the Assistant class :::info[Some features within this guide require a paid plan] If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. ::: -The [`Assistant`](/tools/bolt-js/reference#the-assistantconfig-configuration-object) class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. +The `Assistant` class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. A typical flow would look like: @@ -63,7 +56,7 @@ If you do provide your own `threadContextStore` property, it must feature `get` :::tip[Refer to the [reference docs](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments.] ::: -### Configuring your app to support the `Assistant` class {#configuring-assistant-class} +## Configuring your app to support the `Assistant` class {#configuring-assistant-class} 1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & AI Apps** feature. @@ -77,7 +70,7 @@ If you do provide your own `threadContextStore` property, it must feature `get` * [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) * [`message.im`](/reference/events/message.im) -### Handling a new thread {#handling-new-thread} +## Handling a new thread {#handling-new-thread} When the user opens a new thread with your AI-enabled app, the [`assistant_thread_started`](/reference/events/assistant_thread_started) event will be sent to your app. @@ -122,7 +115,7 @@ def start_assistant_thread( You can send more complex messages to the user — see [Sending Block Kit alongside messages](#block-kit-interactions) for more info. -### Handling thread context changes {#handling-thread-context-changes} +## Handling thread context changes {#handling-thread-context-changes} When the user switches channels, the [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) event will be sent to your app. @@ -137,7 +130,7 @@ from slack_bolt import FileAssistantThreadContextStore assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) ``` -### Handling the user response {#handling-user-response} +## Handling the user response {#handling-user-response} When the user messages your app, the [`message.im`](/reference/events/message.im) event will be sent to your app. @@ -205,7 +198,7 @@ def respond_in_assistant_thread( app.use(assistant) ``` -### Sending Block Kit alongside messages {#block-kit-interactions} +## Sending Block Kit alongside messages {#block-kit-interactions} For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](/messaging/message-metadata/) to trigger subsequent interactions with the user. @@ -331,182 +324,6 @@ def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: ... ``` -See the [_Adding and handling feedback_](#adding-and-handling-feedback) section for adding feedback buttons with Block Kit. - -## Text streaming in messages {#text-streaming} - -Three Web API methods work together to provide users a text streaming experience: - -* the [`chat.startStream`](/reference/methods/chat.startStream) method starts the text stream, -* the [`chat.appendStream`](/reference/methods/chat.appendStream) method appends text to the stream, and -* the [`chat.stopStream`](/reference/methods/chat.stopStream) method stops it. - -Since you're using Bolt for Python, built upon the Python Slack SDK, you can use the [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) utility to streamline all three aspects of streaming in your app's messages. - -The following example uses OpenAI's streaming API with the new `chat_stream()` functionality, but you can substitute it with the AI client of your choice. - - -```python -import os -from typing import List, Dict - -import openai -from openai import Stream -from openai.types.responses import ResponseStreamEvent - -DEFAULT_SYSTEM_CONTENT = """ -You're an assistant in a Slack workspace. -Users in the workspace will ask you to help them write something or to think better about a specific topic. -You'll respond to those questions in a professional way. -When you include markdown text, convert them to Slack compatible ones. -When a prompt has Slack's special syntax like <@USER_ID> or <#CHANNEL_ID>, you must keep them as-is in your response. -""" - -def call_llm( - messages_in_thread: List[Dict[str, str]], - system_content: str = DEFAULT_SYSTEM_CONTENT, -) -> Stream[ResponseStreamEvent]: - openai_client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) - messages = [{"role": "system", "content": system_content}] - messages.extend(messages_in_thread) - response = openai_client.responses.create(model="gpt-4o-mini", input=messages, stream=True) - return response - -@assistant.user_message -def respond_in_assistant_thread( - ... -): - try: - ... - replies = client.conversations_replies( - channel=context.channel_id, - ts=context.thread_ts, - oldest=context.thread_ts, - limit=10, - ) - messages_in_thread: List[Dict[str, str]] = [] - for message in replies["messages"]: - role = "user" if message.get("bot_id") is None else "assistant" - messages_in_thread.append({"role": role, "content": message["text"]}) - - returned_message = call_llm(messages_in_thread) - - streamer = client.chat_stream( - channel=channel_id, - recipient_team_id=team_id, - recipient_user_id=user_id, - thread_ts=thread_ts, - ) - - # Loop over OpenAI response stream - # https://platform.openai.com/docs/api-reference/responses/create - for event in returned_message: - if event.type == "response.output_text.delta": - streamer.append(markdown_text=f"{event.delta}") - else: - continue - - streamer.stop() - - except Exception as e: - logger.exception(f"Failed to handle a user message event: {e}") - say(f":warning: Something went wrong! ({e})") -``` - -## Adding and handling feedback {#adding-and-handling-feedback} - -Use the [feedback buttons block element](/reference/block-kit/block-elements/feedback-buttons-element/) to allow users to immediately provide feedback regarding your app's responses. Here's a quick example: - -```py -from typing import List -from slack_sdk.models.blocks import Block, ContextActionsBlock, FeedbackButtonsElement, FeedbackButtonObject - - -def create_feedback_block() -> List[Block]: - """ - Create feedback block with thumbs up/down buttons - - Returns: - Block Kit context_actions block - """ - blocks: List[Block] = [ - ContextActionsBlock( - elements=[ - FeedbackButtonsElement( - action_id="feedback", - positive_button=FeedbackButtonObject( - text="Good Response", - accessibility_label="Submit positive feedback on this response", - value="good-feedback", - ), - negative_button=FeedbackButtonObject( - text="Bad Response", - accessibility_label="Submit negative feedback on this response", - value="bad-feedback", - ), - ) - ] - ) - ] - return blocks -``` - -Use the `chat_stream` utility to render the feedback block at the bottom of your app's message. - -```js -... - streamer = client.chat_stream( - channel=channel_id, - recipient_team_id=team_id, - recipient_user_id=user_id, - thread_ts=thread_ts, - ) - - # Loop over OpenAI response stream - # https://platform.openai.com/docs/api-reference/responses/create - for event in returned_message: - if event.type == "response.output_text.delta": - streamer.append(markdown_text=f"{event.delta}") - else: - continue - - feedback_block = create_feedback_block() - streamer.stop(blocks=feedback_block) -... -``` - -Then add a response for when the user provides feedback. - -```python -# Handle feedback buttons (thumbs up/down) -def handle_feedback(ack, body, client, logger: logging.Logger): - try: - ack() - message_ts = body["message"]["ts"] - channel_id = body["channel"]["id"] - feedback_type = body["actions"][0]["value"] - is_positive = feedback_type == "good-feedback" - - if is_positive: - client.chat_postEphemeral( - channel=channel_id, - user=body["user"]["id"], - thread_ts=message_ts, - text="We're glad you found this useful.", - ) - else: - client.chat_postEphemeral( - channel=channel_id, - user=body["user"]["id"], - thread_ts=message_ts, - text="Sorry to hear that response wasn't up to par :slightly_frowning_face: Starting a new chat may help with AI mistakes and hallucinations.", - ) - - logger.debug(f"Handled feedback: type={feedback_type}, message_ts={message_ts}") - except Exception as error: - logger.error(f":warning: Something went wrong! {error}") -``` - -## Full example: App Agent Template {#app-agent-template} +See the [_Creating agents: adding and handling feedback_](/tools/bolt-python/concepts/adding-agent-features#adding-and-handling-feedback) section for adding feedback buttons with Block Kit. -Want to see the functionality described throughout this guide in action? We've created a [App Agent Template](https://github.com/slack-samples/bolt-python-assistant-template) repo for you to build off of. +Want to see the functionality described throughout this guide in action? We've created a [App Agent Template](https://github.com/slack-samples/bolt-python-assistant-template) repo for you to build from. \ No newline at end of file diff --git a/docs/english/building-an-app.md b/docs/english/creating-an-app.md similarity index 99% rename from docs/english/building-an-app.md rename to docs/english/creating-an-app.md index bde340961..7f06e9d42 100644 --- a/docs/english/building-an-app.md +++ b/docs/english/creating-an-app.md @@ -1,8 +1,8 @@ --- -sidebar_label: Building an App +sidebar_label: Creating an app --- -# Building an App with Bolt for Python +# Creating an app with Bolt for Python This guide is meant to walk you through getting up and running with a Slack app using Bolt for Python. Along the way, we’ll create a new Slack app, set up your local environment, and develop an app that listens and responds to messages from a Slack workspace. @@ -10,7 +10,7 @@ When you're finished, you'll have created the [Getting Started app](https://gith --- -### Create an app {#create-an-app} +### Create a new app {#create-an-app} First thing's first: before you start developing with Bolt, you'll want to [create a Slack app](https://api.slack.com/apps/new). :::tip[A place to test and learn] diff --git a/docs/english/experiments.md b/docs/english/experiments.md index 681c8cbc6..13adf0a32 100644 --- a/docs/english/experiments.md +++ b/docs/english/experiments.md @@ -28,7 +28,3 @@ def handle_mention(agent: BoltAgent): stream.append(markdown_text="Hello!") stream.stop() ``` - -### Limitations - -The `chat_stream()` method currently only works when the `thread_ts` field is available in the event context (DMs and threaded replies). Top-level channel messages do not have a `thread_ts` field, and the `ts` field is not yet provided to `BoltAgent`. \ No newline at end of file diff --git a/docs/english/getting-started.md b/docs/english/getting-started.md index 934dd3bae..6964df23b 100644 --- a/docs/english/getting-started.md +++ b/docs/english/getting-started.md @@ -279,55 +279,10 @@ This will open the following page: On these pages you're free to make changes such as updating your app icon, configuring app features, and perhaps even distributing your app! -## Adding AI features {#ai-features} - -Now that you're familiar with a basic app setup, try it out again, this time using the AI agent template! - - - - -Get started with the agent template: - -```sh -$ slack create ai-app --template slack-samples/bolt-python-assistant-template -$ cd ai-app -``` - - - - -Get started with the agent template: - -```sh -$ git clone https://github.com/slack-samples/bolt-python-assistant-template ai-app -$ cd ai-app -``` - -Using this method, be sure to set the app and bot tokens as we did in the [Running the app](#running-the-app) section above. - - - - -Once the project is created, update the `.env.sample` file by setting the `OPENAI_API_KEY` with the value of your key and removing the `.sample` from the file name. - -In the `ai` folder of this app, you'll find default instructions for the LLM and an OpenAI client setup. - -The `listeners` include utilities intended for messaging with an LLM. Those are outlined in detail in the guide to [Using AI in apps](/tools/bolt-python/concepts/ai-apps) and [Sending messages](/tools/bolt-python/concepts/message-sending). - ## Next steps {#next-steps} -Congrats once more on getting up and running with this quick start. - -:::info[Dive deeper] - -Follow along with the steps that went into making this app on the [building an app](/tools/bolt-python/building-an-app) guide for an educational overview. - -::: - You can now continue customizing your app with various features to make it right for whatever job's at hand. Here are some ideas about what to explore next: -- Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. See the full events reference [here](/reference/events). -- Bolt allows you to call [Web API](/tools/bolt-python/concepts/web-api) methods with the client attached to your app. There are [over 200 methods](/reference/methods) available. -- Learn more about the different [token types](/authentication/tokens) and [authentication setups](/tools/bolt-python/concepts/authenticating-oauth). Your app might need different tokens depending on the actions you want to perform or for installations to multiple workspaces. -- Receive events using HTTP for various deployment methods, such as deploying to Heroku or AWS Lambda. -- Read on [app design](/surfaces/app-design) and compose fancy messages with blocks using [Block Kit Builder](https://app.slack.com/block-kit-builder) to prototype messages. +- Follow along with the steps that went into making this app on the [creating an app](/tools/bolt-python/creating-an-app) guide for an educational overview. +- Check out the [Agent quickstart](/ai/agent-quickstart) to get up and running with an agent. +- Browse our [curated catalog of samples](/samples) for more apps to use as a starting point for development. \ No newline at end of file diff --git a/docs/english/tutorial/ai-chatbot/ai-chatbot.md b/docs/english/tutorial/ai-chatbot/ai-chatbot.md index 72005f817..2fcc16e9a 100644 --- a/docs/english/tutorial/ai-chatbot/ai-chatbot.md +++ b/docs/english/tutorial/ai-chatbot/ai-chatbot.md @@ -1,64 +1,72 @@ # AI Chatbot -In this tutorial, you'll learn how to bring the power of AI into your Slack workspace using a chatbot called Bolty that uses Anthropic or OpenAI. Here's what we'll do with this sample app: - -1. Create your app from an app manifest and clone a starter template -2. Set up and run your local project -3. Create a workflow using Workflow Builder to summarize messages in conversations -4. Select your preferred API and model to customize Bolty's responses -5. Interact with Bolty via direct message, the `/ask-bolty` slash command, or by mentioning the app in conversations +In this tutorial, you'll learn how to bring the power of AI into your Slack workspace using a chatbot called Bolty that uses Anthropic or OpenAI. + +With Bolty, users can: + +- send direct messages to Bolty and get AI-powered responses in response, +- use the `/ask-bolty` slash command to ask Bolty questions, and +- receive channel summaries when joining new channels. + +Intrigued? First, grab your tools by following the three steps below. + +import QuickstartGuide from '@site/src/components/QuickstartGuide'; + + + +
      ## Prerequisites {#prereqs} -Before getting started, you will need the following: +You will also need the following: -- a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now — you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. +- a development workspace where you have permissions to install apps. If you don’t have a workspace you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. - a development environment with [Python 3.7](https://www.python.org/downloads/) or later. - an Anthropic or OpenAI account with sufficient credits, and in which you have generated a secret key. -**Skip to the code** -If you'd rather skip the tutorial and just head straight to the code, you can use our [Bolt for Python AI Chatbot sample](https://github.com/slack-samples/bolt-python-ai-chatbot) as a template. - -## Creating your app {#create-app} - -1. Navigate to the [app creation page](https://api.slack.com/apps/new) and select **From a manifest**. -2. Select the workspace you want to install the application in. -3. Copy the contents of the [`manifest.json`](https://github.com/slack-samples/bolt-python-ai-chatbot/blob/main/manifest.json) file into the text box that says **Paste your manifest code here** (within the **JSON** tab) and click **Next**. -4. Review the configuration and click **Create**. -5. You're now in your app configuration's **Basic Information** page. Navigate to the **Install App** link in the left nav and click **Install to Workspace**, then **Allow** on the screen that follows. - ### Obtaining and storing your environment variables {#environment-variables} Before you'll be able to successfully run the app, you'll need to first obtain and set some environment variables. -#### Slack tokens {#slack-tokens} - -From your app's page on [app settings](https://api.slack.com/apps) collect an app and bot token: - -1. On the **Install App** page, copy your **Bot User OAuth Token**. You will store this in your environment as `SLACK_BOT_TOKEN` (we'll get to that next). -2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](/reference/scopes/connections.write) scope, name the token, and click **Generate**. (For more details, refer to [understanding OAuth scopes for bots](/authentication/tokens#bot)). Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`. - -To store your tokens and environment variables, run the following commands in the terminal. Replace the placeholder values with your bot and app tokens collected above: - -**For macOS** - -```bash -export SLACK_BOT_TOKEN= -export SLACK_APP_TOKEN= -``` - -**For Windows** - -```pwsh -set SLACK_BOT_TOKEN= -set SLACK_APP_TOKEN= -``` - #### Provider tokens {#provider-tokens} Models from different AI providers are available if the corresponding environment variable is added as shown in the sections below. -##### Anthropic {#anthropic} + + To interact with Anthropic models, navigate to your Anthropic account dashboard to [create an API key](https://console.anthropic.com/settings/keys), then export the key as follows: @@ -66,7 +74,8 @@ To interact with Anthropic models, navigate to your Anthropic account dashboard export ANTHROPIC_API_KEY= ``` -##### Google Cloud Vertex AI {#google-cloud-vertex-ai} + + To use Google Cloud Vertex AI, [follow this quick start](https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal#expandable-1) to create a project for sending requests to the Gemini API, then gather [Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc) with the strategy to match your development environment. @@ -79,7 +88,8 @@ export VERTEX_AI_LOCATION= The project location can be located under the **Region** on the [Vertex AI](https://console.cloud.google.com/vertex-ai) dashboard, as well as more details about available Gemini models. -##### OpenAI {#openai} + + Unlock the OpenAI models from your OpenAI account dashboard by clicking [create a new secret key](https://platform.openai.com/api-keys), then export the key like so: @@ -87,49 +97,46 @@ Unlock the OpenAI models from your OpenAI account dashboard by clicking [create export OPENAI_API_KEY= ``` -## Setting up and running your local project {#configure-project} - -Clone the starter template onto your machine by running the following command: - -```bash -git clone https://github.com/slack-samples/bolt-python-ai-chatbot.git -``` + + -Change into the new project directory: +## Setting up and running your local project {#configure-project} -```bash -cd bolt-python-ai-chatbot -``` Start your Python virtual environment: -**For macOS** + + ```bash python3 -m venv .venv source .venv/bin/activate ``` -**For Windows** + + ```bash py -m venv .venv .venv\Scripts\activate ``` + + + Install the required dependencies: ```bash pip install -r requirements.txt ``` -Start your local server: +Run your app locally: ```bash -python app.py +slack run ``` -If your app is up and running, you'll see a message that says "⚡️ Bolt app is running!" +If your app is indeed up and running, you'll see a message that says "⚡️ Bolt app is running!" ## Choosing your provider {#provider} @@ -235,5 +242,4 @@ You can also navigate to **Bolty** in your **Apps** list and select the **Messag Congratulations! You've successfully integrated the power of AI into your workspace. Check out these links to take the next steps in your Bolt for Python journey. - To learn more about Bolt for Python, refer to the [Getting started](/tools/bolt-python/getting-started) documentation. -- For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. -- To use the Bolt for Python SDK to develop on the automations platform, refer to the [Create a workflow step for Workflow Builder: Bolt for Python](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) tutorial. +- For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. \ No newline at end of file diff --git a/docs/japanese/concepts/assistant.md b/docs/japanese/concepts/assistant.md deleted file mode 100644 index 664108607..000000000 --- a/docs/japanese/concepts/assistant.md +++ /dev/null @@ -1,227 +0,0 @@ -# エージェント・アシスタント - -このページは、Bolt を使ってエージェント・アシスタントを実装するための方法を紹介します。この機能に関する一般的な情報については、[こちらのドキュメントページ(英語)](/ai/)を参照してください。 - -この機能を実装するためには、まず[アプリの設定画面](https://api.slack.com/apps)で **Agents & Assistants** 機能を有効にし、**OAuth & Permissions** のページで [`assistant:write`](/reference/scopes/assistant.write)、[chat:write](/reference/scopes/chat.write)、[`im:history`](/reference/scopes/im.history) を**ボットの**スコープに追加し、**Event Subscriptions** のページで [`assistant_thread_started`](/reference/events/assistant_thread_started)、[`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed)、[`message.im`](/reference/events/message.im) イベントを有効にしてください。 - -また、この機能は Slack の有料プランでのみ利用可能です。もし開発用の有料プランのワークスペースをお持ちでない場合は、[Developer Program](https://api.slack.com/developer-program) に参加し、全ての有料プラン向け機能を利用可能なサンドボックス環境をつくることができます。 - -ユーザーとのアシスタントスレッド内でのやりとりを処理するには、`assistant_thread_started`、`assistant_thread_context_changed`、`message` イベントの `app.event(...)` リスナーを使うことも可能ですが、Bolt はよりシンプルなアプローチを提供しています。`Assistant` インスタンスを作り、それに必要なイベントリスナーを追加し、最後にこのアシスタント設定を `App` インスタンスに渡すだけでよいのです。 - -```python -assistant = Assistant() - -# ユーザーがアシスタントスレッドを開いたときに呼び出されます -@assistant.thread_started -def start_assistant_thread(say: Say, set_suggested_prompts: SetSuggestedPrompts): - # ユーザーに対して最初の返信を送信します - say(":wave: Hi, how can I help you today?") - - # プロンプト例を送るのは必須ではありません - set_suggested_prompts( - prompts=[ - # もしプロンプトが長い場合は {"title": "表示する短いラベル", "message": "完全なプロンプト"} を使うことができます - "What does SLACK stand for?", - "When Slack was released?", - ], - ) - -# ユーザーがスレッド内で返信したときに呼び出されます -@assistant.user_message -def respond_in_assistant_thread( - payload: dict, - logger: logging.Logger, - context: BoltContext, - set_status: SetStatus, - say: Say, - client: WebClient, -): - try: - # ユーザーにこのbotがリクエストを受信して作業中であることを伝えます - set_status("is typing...") - - # 会話の履歴を取得します - replies_in_thread = client.conversations_replies( - channel=context.channel_id, - ts=context.thread_ts, - oldest=context.thread_ts, - limit=10, - ) - messages_in_thread: List[Dict[str, str]] = [] - for message in replies_in_thread["messages"]: - role = "user" if message.get("bot_id") is None else "assistant" - messages_in_thread.append({"role": role, "content": message["text"]}) - - # プロンプトと会話の履歴を LLM に渡します(この call_llm はあなた自身のコードです) - returned_message = call_llm(messages_in_thread) - - # 結果をアシスタントスレッドに送信します - say(text=returned_message) - - except Exception as e: - logger.exception(f"Failed to respond to an inquiry: {e}") - # エラーになった場合は必ずメッセージを送信するようにしてください - # そうしなかった場合、'is typing...' の表示のままになってしまい、ユーザーは会話を続けることができなくなります - say(f":warning: Sorry, something went wrong during processing your request (error: {e})") - -# このミドルウェアを Bolt アプリに追加します -app.use(assistant) -``` - -リスナーに指定可能な引数の一覧は[モジュールドキュメント](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html)を参考にしてください。 - -ユーザーがチャンネルの横でアシスタントスレッドを開いた場合、そのチャンネルの情報は、そのスレッドの `AssistantThreadContext` データとして保持され、 `get_thread_context` ユーティリティを使ってアクセスすることができます。Bolt がこのユーティリティを提供している理由は、後続のユーザーメッセージ投稿のイベントペイロードに最新のスレッドのコンテキスト情報は含まれないためです。そのため、アプリはコンテキスト情報が変更されたタイミングでそれを何らかの方法で保存し、後続のメッセージイベントのリスナーコードから参照できるようにする必要があります。 - -そのユーザーがチャンネルを切り替えた場合、`assistant_thread_context_changed` イベントがあなたのアプリに送信されます。(上記のコード例のように)組み込みの `Assistant` ミドルウェアをカスタム設定なしで利用している場合、この更新されたチャンネル情報は、自動的にこのアシスタントボットからの最初の返信のメッセージメタデータとして保存されます。これは、組み込みの仕組みを使う場合は、このコンテキスト情報を自前で用意したデータストアに保存する必要はないということです。この組み込みの仕組みの唯一の短所は、追加の Slack API 呼び出しによる処理時間のオーバーヘッドです。具体的には `get_thread_context` を実行したときに、この保存されたメッセージメタデータにアクセスするために `conversations.history` API が呼び出されます。 - -このデータを別の場所に保存したい場合、自前の `AssistantThreadContextStore` 実装を `Assistant` のコンストラクターに渡すことができます。リファレンス実装として、`FileAssistantThreadContextStore` というローカルファイルシステムを使って実装を提供しています: - -```python -# これはあくまで例であり、自前のものを渡すことができます -from slack_bolt import FileAssistantThreadContextStore -assistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) -``` - -このリファレンス実装はローカルファイルに依存しており、本番環境での利用は推奨しません。本番アプリでは `AssistantThreadContextStore` を継承した自前のクラスを使うようにしてください。 - -最後に、動作する完全なサンプルコード例を確認したい場合は、私たちが GitHub 上で提供している[サンプルアプリのリポジトリ](https://github.com/slack-samples/bolt-python-assistant-template)をチェックしてみてください。 - -## アシスタントスレッドでの Block Kit インタラクション - -より高度なユースケースでは、上のようなプロンプト例の提案ではなく Block Kit のボタンなどを使いたいという場合があるかもしれません。そして、後続の処理のために[構造化されたメッセージメタデータ](/messaging/message-metadata/)を含むメッセージを送信したいという場合もあるでしょう。 - -例えば、アプリが最初の返信で「参照しているチャンネルを要約」のようなボタンを表示し、ユーザーがそれをクリックして、より詳細な情報(例:要約するメッセージ数・日数、要約の目的など)を送信、アプリがそれを構造化されたメータデータに整理した上でリクエスト内容をボットのメッセージとして送信するようなシナリオです。 - -デフォルトでは、アプリはそのアプリ自身から送信したボットメッセージに応答することはできません(Bolt にはあらかじめ無限ループを防止する制御が入っているため)。`ignoring_self_assistant_message_events_enabled=False` を `App` のコンストラクターに渡し、`bot_message` リスナーを `Assistant` ミドルウェアに追加すると、上記の例のようなリクエストを伝えるボットメッセージを使って処理を継続することができるようになります。 - -```python -app = App( - token=os.environ["SLACK_BOT_TOKEN"], - # bot message を受け取るには必ずこれを指定してください - ignoring_self_assistant_message_events_enabled=False, -) - -assistant = Assistant() - -# リスナーに指定可能な引数の一覧は https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html を参照してください - -@assistant.thread_started -def start_assistant_thread(say: Say): - say( - text=":wave: Hi, how can I help you today?", - blocks=[ - { - "type": "section", - "text": {"type": "mrkdwn", "text": ":wave: Hi, how can I help you today?"}, - }, - { - "type": "actions", - "elements": [ - # 複数のボタンを配置することが可能です - { - "type": "button", - "action_id": "assistant-generate-random-numbers", - "text": {"type": "plain_text", "text": "Generate random numbers"}, - "value": "clicked", - }, - ], - }, - ], - ) - -# 上のボタンがクリックされたときに実行されます -@app.action("assistant-generate-random-numbers") -def configure_random_number_generation(ack: Ack, client: WebClient, body: dict): - ack() - client.views_open( - trigger_id=body["trigger_id"], - view={ - "type": "modal", - "callback_id": "configure_assistant_summarize_channel", - "title": {"type": "plain_text", "text": "My Assistant"}, - "submit": {"type": "plain_text", "text": "Submit"}, - "close": {"type": "plain_text", "text": "Cancel"}, - # アシスタントスレッドの情報を app.view リスナーに引き継ぎます - "private_metadata": json.dumps( - { - "channel_id": body["channel"]["id"], - "thread_ts": body["message"]["thread_ts"], - } - ), - "blocks": [ - { - "type": "input", - "block_id": "num", - "label": {"type": "plain_text", "text": "# of outputs"}, - # 自然言語のテキストではなく、あらかじめ決められた形式の入力を受け取ることができます - "element": { - "type": "static_select", - "action_id": "input", - "placeholder": {"type": "plain_text", "text": "How many numbers do you need?"}, - "options": [ - {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, - {"text": {"type": "plain_text", "text": "10"}, "value": "10"}, - {"text": {"type": "plain_text", "text": "20"}, "value": "20"}, - ], - "initial_option": {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, - }, - } - ], - }, - ) - -# 上のモーダルが送信されたときに実行されます -@app.view("configure_assistant_summarize_channel") -def receive_random_number_generation_details(ack: Ack, client: WebClient, payload: dict): - ack() - num = payload["state"]["values"]["num"]["input"]["selected_option"]["value"] - thread = json.loads(payload["private_metadata"]) - - # 構造化された入力情報とともにボットのメッセージを送信します - # 以下の assistant.bot_message リスナーが処理を継続します - # このリスナー内で処理したい場合はそれでも構いません! - # bot_message リスナーが必要ない場合は ignoring_self_assistant_message_events_enabled=False を設定する必要はありません - client.chat_postMessage( - channel=thread["channel_id"], - thread_ts=thread["thread_ts"], - text=f"OK, you need {num} numbers. I will generate it shortly!", - metadata={ - "event_type": "assistant-generate-random-numbers", - "event_payload": {"num": int(num)}, - }, - ) - -# このアプリのボットユーザーがメッセージを送信したときに実行されます -@assistant.bot_message -def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: Say, payload: dict): - try: - if payload.get("metadata", {}).get("event_type") == "assistant-generate-random-numbers": - # 上の random-number-generation リクエストを処理します - set_status("is generating an array of random numbers...") - time.sleep(1) - nums: Set[str] = set() - num = payload["metadata"]["event_payload"]["num"] - while len(nums) < num: - nums.add(str(random.randint(1, 100))) - say(f"Here you are: {', '.join(nums)}") - else: - # それ以外のパターンでは何もしません - # さらに他のパターンを追加する場合、メッセージ送信の無限ループを起こさないよう注意して実装してください - pass - - except Exception as e: - logger.exception(f"Failed to respond to an inquiry: {e}") - -# ユーザーが返信したときに実行されます -@assistant.user_message -def respond_to_user_messages(logger: logging.Logger, set_status: SetStatus, say: Say): - try: - set_status("is typing...") - say("Please use the buttons in the first reply instead :bow:") - except Exception as e: - logger.exception(f"Failed to respond to an inquiry: {e}") - say(f":warning: Sorry, something went wrong during processing your request (error: {e})") - -# このミドルウェアを Bolt アプリに追加します -app.use(assistant) -``` \ No newline at end of file From 2266ac7d9ea8c36c2b17266eb6e1dc45578372aa Mon Sep 17 00:00:00 2001 From: Sascha Buehrle <47737812+saschabuehrle@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:53:11 +0200 Subject: [PATCH 215/282] fix: handle malformed user/view payloads in extract_team_id (#1481) --- slack_bolt/request/internals.py | 4 ++-- tests/slack_bolt/request/test_internals.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/slack_bolt/request/internals.py b/slack_bolt/request/internals.py index 15d1e7367..e0863a713 100644 --- a/slack_bolt/request/internals.py +++ b/slack_bolt/request/internals.py @@ -112,9 +112,9 @@ def extract_team_id(payload: Dict[str, Any]) -> Optional[str]: if isinstance(payload.get("event"), dict): return extract_team_id(payload["event"]) if isinstance(payload.get("user"), dict): - return payload["user"]["team_id"] + return payload["user"].get("team_id") if isinstance(payload.get("view"), dict): - return payload["view"]["team_id"] + return payload["view"].get("team_id") return None diff --git a/tests/slack_bolt/request/test_internals.py b/tests/slack_bolt/request/test_internals.py index 8cccf0431..31ac35bdd 100644 --- a/tests/slack_bolt/request/test_internals.py +++ b/tests/slack_bolt/request/test_internals.py @@ -1253,8 +1253,10 @@ def test_extraction_functions_invalid_dict_keys(self): invalid_payloads = { "event": {"event": "some_event_type"}, "user": {"user": "U12345"}, + "user_missing_team_id": {"user": {"id": "U12345"}}, "team": {"team": "T12345"}, "view": {"view": "V12345"}, + "view_missing_team_id": {"view": {"id": "V12345"}}, "message": {"message": "some text"}, "item": {"item": "item_id"}, "function_data": {"function_data": "fd_123"}, From 288cf4aebebd9ed38dc3b1d01bd9ffa948abd1ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 01:42:09 +0000 Subject: [PATCH 216/282] chore(deps): bump dependabot/fetch-metadata from 3.0.0 to 3.1.0 (#1485) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependencies.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 9666057aa..aafc2c0af 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Collect metadata id: metadata - uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v3.0.0 + uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Approve From 80d8670b4c703131a0cce7856eab4016cf7977e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 01:45:56 +0000 Subject: [PATCH 217/282] chore(deps): bump pypa/gh-action-pypi-publish from 1.13.0 to 1.14.0 (#1489) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index dfc224c83..33bc52f14 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -60,7 +60,7 @@ jobs: - name: Publish release distributions to test.pypi.org # Using OIDC for PyPI publishing (no API tokens needed) # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: repository-url: https://test.pypi.org/legacy/ @@ -84,4 +84,4 @@ jobs: - name: Publish release distributions to pypi.org # Using OIDC for PyPI publishing (no API tokens needed) # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 From 9fa5c76137e339bd05765c42496c6f33404685a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 01:49:35 +0000 Subject: [PATCH 218/282] chore(deps): bump slackapi/slack-github-action from 3.0.1 to 3.0.3 (#1487) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 6d504ea83..b158c72ea 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -179,7 +179,7 @@ jobs: if: ${{ !success() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' }} steps: - name: Send notifications of failing tests - uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1 + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: errors: true webhook: ${{ secrets.SLACK_REGRESSION_FAILURES_WEBHOOK_URL }} From 1c4d0c597372e4b53fa4d5ea838c27ea971a4768 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 01:53:30 +0000 Subject: [PATCH 219/282] chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 (#1486) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 33bc52f14..964eb2c77 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -33,7 +33,7 @@ jobs: scripts/build_pypi_package.sh - name: Persist dist folder - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: release-dist path: dist/ From 7730735278374fc171be432028cae37511c34e4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 13:57:27 +0000 Subject: [PATCH 220/282] chore(deps): update boddle requirement from <0.3,>=0.2 to >=0.2.9,<0.3 (#1488) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter_testing.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter_testing.txt b/requirements/adapter_testing.txt index c497a1f3f..dd4a1cf84 100644 --- a/requirements/adapter_testing.txt +++ b/requirements/adapter_testing.txt @@ -1,5 +1,5 @@ # pip install -r requirements/adapter_testing.txt moto>=3,<6 # For AWS tests docker>=5,<8 # Used by moto -boddle>=0.2,<0.3 # For Bottle app tests +boddle>=0.2.9,<0.3 # For Bottle app tests sanic-testing>=0.7 From 1def8079e570fb6176c100cf7079524816597ad6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:24:06 +0000 Subject: [PATCH 221/282] chore(deps): update werkzeug requirement from <4,>=2 to >=3.1.8,<4 (#1490) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin --- requirements/adapter.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index c19c7713b..baa8c1746 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -11,7 +11,8 @@ falcon>=2,<5; python_version<"3.11" falcon>=3.1.1,<5; python_version>="3.11" fastapi>=0.70.0,<1 Flask>=1,<4 -Werkzeug>=2,<4 +Werkzeug>=2,<3; python_version<"3.9" +Werkzeug>=3.1.8,<4; python_version>="3.9" pyramid>=1,<3 setuptools<82 # Pinned: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 From 6b15876e9282af2c979a416d611441dc058dbdeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:38:56 +0000 Subject: [PATCH 222/282] chore(deps): update falcon requirement from <5,>=2 to >=4.2.0,<5 (#1491) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index baa8c1746..82eea9ef9 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -7,8 +7,8 @@ chalice>=1.28,<2; cheroot<12 CherryPy>=18,<19 Django>=3,<6 -falcon>=2,<5; python_version<"3.11" -falcon>=3.1.1,<5; python_version>="3.11" +falcon>=2,<4; python_version<"3.9" +falcon>=4.2.0,<5; python_version>="3.9" fastapi>=0.70.0,<1 Flask>=1,<4 Werkzeug>=2,<3; python_version<"3.9" From ae0ba5796167788052b610765117ebdae0ac5450 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:53:48 +0000 Subject: [PATCH 223/282] chore(deps): update chalice requirement from <2,>=1.28 to >=1.32.0,<2 (#1492) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 82eea9ef9..2564aae79 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -3,7 +3,8 @@ # used only under slack_bolt/adapter boto3<=2 bottle>=0.12,<1 -chalice>=1.28,<2; +chalice>=1.28,<1.31; python_version<"3.9" +chalice>=1.32.0,<2; python_version>="3.9" cheroot<12 CherryPy>=18,<19 Django>=3,<6 From 07fb5891d4983f160c55299feff15894d7b5db90 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 11 May 2026 12:26:56 -0400 Subject: [PATCH 224/282] fix: mock module level time to improve stability (#1497) --- tests/scenario_tests/test_function.py | 42 +++++++++++++++------ tests/scenario_tests_async/test_function.py | 42 ++++++++++++++------- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/tests/scenario_tests/test_function.py b/tests/scenario_tests/test_function.py index 5a4fc2685..1e5e1c423 100644 --- a/tests/scenario_tests/test_function.py +++ b/tests/scenario_tests/test_function.py @@ -7,6 +7,7 @@ from slack_sdk.signature import SignatureVerifier from slack_sdk.web import WebClient +import slack_bolt.listener.thread_runner as runner_module from slack_bolt.app import App from slack_bolt.request import BoltRequest from tests.mock_web_api_server import ( @@ -53,9 +54,9 @@ def build_request_from_body(self, message_body: dict) -> BoltRequest: timestamp, body = str(int(time.time())), json.dumps(message_body) return BoltRequest(body=body, headers=self.build_headers(timestamp, body)) - def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock: Mock, sleep_mock: Mock): - monkeypatch.setattr(time, "time", time_mock) - monkeypatch.setattr(time, "sleep", sleep_mock) + def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock, sleep_mock): + monkeypatch.setattr(runner_module.time, "time", time_mock) + monkeypatch.setattr(runner_module.time, "sleep", sleep_mock) def test_valid_callback_id_success(self): app = App( @@ -138,10 +139,20 @@ def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkeypatch) app.function("reverse", auto_acknowledge=False)(just_no_ack) request = self.build_request_from_body(function_body) + + elapsed_seconds = 0 + + def fake_time(): + return float(elapsed_seconds) + + def fake_sleep(duration): + nonlocal elapsed_seconds + elapsed_seconds += 1 + self.setup_time_mocks( monkeypatch=monkeypatch, - time_mock=Mock(side_effect=[current_time for current_time in range(100)]), - sleep_mock=Mock(), + time_mock=fake_time, + sleep_mock=Mock(side_effect=fake_sleep), ) response = app.dispatch(request) @@ -158,20 +169,29 @@ def test_function_handler_timeout(self, monkeypatch): app.function("reverse", auto_acknowledge=False, ack_timeout=timeout)(just_no_ack) request = self.build_request_from_body(function_body) - sleep_mock = Mock() + elapsed_seconds = 0 + + def fake_time(): + return float(elapsed_seconds) + + def fake_sleep(duration): + nonlocal elapsed_seconds + elapsed_seconds += 1 + self.setup_time_mocks( monkeypatch=monkeypatch, - time_mock=Mock(side_effect=[current_time for current_time in range(100)]), - sleep_mock=sleep_mock, + time_mock=fake_time, + sleep_mock=Mock(side_effect=fake_sleep), ) response = app.dispatch(request) assert response.status == 404 assert_auth_test_count(self, 1) - assert ( - sleep_mock.call_count == timeout - ), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times" + assert elapsed_seconds == timeout + 1, ( + f"Expected handler to time out after {timeout + 1} time.sleep calls, " + f"but it was called {elapsed_seconds} times" + ) def test_warning_when_timeout_improperly_set(self, caplog): app = App( diff --git a/tests/scenario_tests_async/test_function.py b/tests/scenario_tests_async/test_function.py index abf3ffb48..ce9080e36 100644 --- a/tests/scenario_tests_async/test_function.py +++ b/tests/scenario_tests_async/test_function.py @@ -8,6 +8,7 @@ from slack_sdk.signature import SignatureVerifier from slack_sdk.web.async_client import AsyncWebClient +import slack_bolt.listener.asyncio_runner as async_runner_module from slack_bolt.app.async_app import AsyncApp from slack_bolt.request.async_request import AsyncBoltRequest from tests.mock_web_api_server import ( @@ -19,10 +20,6 @@ from tests.utils import remove_os_env_temporarily, restore_os_env -async def fake_sleep(seconds): - pass - - class TestAsyncFunction: signing_secret = "secret" valid_token = "xoxb-valid" @@ -60,9 +57,9 @@ def build_request_from_body(self, message_body: dict) -> AsyncBoltRequest: timestamp, body = str(int(time.time())), json.dumps(message_body) return AsyncBoltRequest(body=body, headers=self.build_headers(timestamp, body)) - def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock: Mock, sleep_mock: MagicMock): - monkeypatch.setattr(time, "time", time_mock) - monkeypatch.setattr(asyncio, "sleep", sleep_mock) + def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock, sleep_mock): + monkeypatch.setattr(async_runner_module.time, "time", time_mock) + monkeypatch.setattr(async_runner_module.asyncio, "sleep", sleep_mock) @pytest.mark.asyncio async def test_mock_server_is_running(self): @@ -146,9 +143,18 @@ async def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkey app.function("reverse", auto_acknowledge=False)(just_no_ack) request = self.build_request_from_body(function_body) + elapsed_seconds = 0 + + def fake_time(): + return float(elapsed_seconds) + + async def fake_sleep(duration): + nonlocal elapsed_seconds + elapsed_seconds += 1 + self.setup_time_mocks( monkeypatch=monkeypatch, - time_mock=Mock(side_effect=[current_time for current_time in range(100)]), + time_mock=fake_time, sleep_mock=MagicMock(side_effect=fake_sleep), ) @@ -167,20 +173,28 @@ async def test_function_handler_timeout(self, monkeypatch): app.function("reverse", auto_acknowledge=False, ack_timeout=timeout)(just_no_ack) request = self.build_request_from_body(function_body) - sleep_mock = MagicMock(side_effect=fake_sleep) + elapsed_seconds = 0 + + def fake_time(): + return float(elapsed_seconds) + + async def fake_sleep(duration): + nonlocal elapsed_seconds + elapsed_seconds += 1 + self.setup_time_mocks( monkeypatch=monkeypatch, - time_mock=Mock(side_effect=[current_time for current_time in range(100)]), - sleep_mock=sleep_mock, + time_mock=fake_time, + sleep_mock=MagicMock(side_effect=fake_sleep), ) response = await app.async_dispatch(request) assert response.status == 404 await assert_auth_test_count_async(self, 1) - assert ( - sleep_mock.call_count == timeout - ), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times" + assert elapsed_seconds == timeout + 1, ( + f"Expected handler to time out after {timeout + 1} sleep calls, " f"but it was called {elapsed_seconds} times" + ) @pytest.mark.asyncio async def test_warning_when_timeout_improperly_set(self, caplog): From c0dd462964b3d7d78713608fa58e7096bb2be673 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Wed, 13 May 2026 10:10:51 -0700 Subject: [PATCH 225/282] fix: propagate Socket Mode retry_attempt and retry_reason to BoltRequest headers (#1498) --- .../adapter/socket_mode/async_internals.py | 3 +- slack_bolt/adapter/socket_mode/internals.py | 13 ++++++- .../socket_mode/test_internals.py | 39 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/adapter_tests/socket_mode/test_internals.py diff --git a/slack_bolt/adapter/socket_mode/async_internals.py b/slack_bolt/adapter/socket_mode/async_internals.py index c2965f766..00c33bf58 100644 --- a/slack_bolt/adapter/socket_mode/async_internals.py +++ b/slack_bolt/adapter/socket_mode/async_internals.py @@ -8,13 +8,14 @@ from slack_sdk.socket_mode.request import SocketModeRequest from slack_sdk.socket_mode.response import SocketModeResponse +from slack_bolt.adapter.socket_mode.internals import build_retry_headers from slack_bolt.app.async_app import AsyncApp from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse async def run_async_bolt_app(app: AsyncApp, req: SocketModeRequest): - bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload) + bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload, headers=build_retry_headers(req)) bolt_resp: BoltResponse = await app.async_dispatch(bolt_req) return bolt_resp diff --git a/slack_bolt/adapter/socket_mode/internals.py b/slack_bolt/adapter/socket_mode/internals.py index 8eb751b4d..9d6c3f898 100644 --- a/slack_bolt/adapter/socket_mode/internals.py +++ b/slack_bolt/adapter/socket_mode/internals.py @@ -3,6 +3,7 @@ import json import logging from time import time +from typing import Dict, Optional, Sequence, Union from slack_sdk.socket_mode.client import BaseSocketModeClient from slack_sdk.socket_mode.request import SocketModeRequest @@ -13,8 +14,18 @@ from slack_bolt.response import BoltResponse +def build_retry_headers(req: SocketModeRequest) -> Optional[Dict[str, Union[str, Sequence[str]]]]: + # Mirror the HTTP mode retry headers so middleware/listeners can detect Events API retries + headers: Dict[str, Union[str, Sequence[str]]] = {} + if req.retry_attempt is not None: + headers["x-slack-retry-num"] = str(req.retry_attempt) + if req.retry_reason is not None: + headers["x-slack-retry-reason"] = req.retry_reason + return headers or None + + def run_bolt_app(app: App, req: SocketModeRequest): - bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload) + bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload, headers=build_retry_headers(req)) bolt_resp: BoltResponse = app.dispatch(bolt_req) return bolt_resp diff --git a/tests/adapter_tests/socket_mode/test_internals.py b/tests/adapter_tests/socket_mode/test_internals.py new file mode 100644 index 000000000..2289196d4 --- /dev/null +++ b/tests/adapter_tests/socket_mode/test_internals.py @@ -0,0 +1,39 @@ +from slack_sdk.socket_mode.request import SocketModeRequest + +from slack_bolt.adapter.socket_mode.internals import build_retry_headers, run_bolt_app + + +class TestSocketModeInternals: + def test_build_retry_headers_without_retry(self): + req = SocketModeRequest(type="events_api", envelope_id="e1", payload={"type": "event_callback"}) + assert build_retry_headers(req) is None + + def test_build_retry_headers_with_retry(self): + req = SocketModeRequest( + type="events_api", + envelope_id="e1", + payload={"type": "event_callback"}, + retry_attempt=2, + retry_reason="http_timeout", + ) + headers = build_retry_headers(req) + assert headers == {"x-slack-retry-num": "2", "x-slack-retry-reason": "http_timeout"} + + def test_run_bolt_app_propagates_retry_headers(self): + captured = {} + + class FakeApp: + def dispatch(self, bolt_req): + captured["headers"] = bolt_req.headers + return None + + req = SocketModeRequest( + type="events_api", + envelope_id="e1", + payload={"type": "event_callback", "event": {"type": "app_mention"}}, + retry_attempt=1, + retry_reason="http_timeout", + ) + run_bolt_app(FakeApp(), req) + assert captured["headers"]["x-slack-retry-num"] == ["1"] + assert captured["headers"]["x-slack-retry-reason"] == ["http_timeout"] From b0eda4464042ed1f9f114a1ceda0d3cc0b4ddd19 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 14 May 2026 12:40:07 -0400 Subject: [PATCH 226/282] chore: improve testing around socket-mode header synthesization (#1499) --- .../adapter/socket_mode/async_internals.py | 4 +-- slack_bolt/adapter/socket_mode/internals.py | 4 +-- .../socket_mode/test_interactions_builtin.py | 10 +++++++- .../test_interactions_websocket_client.py | 10 +++++++- .../socket_mode/test_internals.py | 25 +++---------------- .../socket_mode/test_async_aiohttp.py | 10 +++++++- .../socket_mode/test_async_websockets.py | 10 +++++++- 7 files changed, 43 insertions(+), 30 deletions(-) diff --git a/slack_bolt/adapter/socket_mode/async_internals.py b/slack_bolt/adapter/socket_mode/async_internals.py index 00c33bf58..428ab437c 100644 --- a/slack_bolt/adapter/socket_mode/async_internals.py +++ b/slack_bolt/adapter/socket_mode/async_internals.py @@ -8,14 +8,14 @@ from slack_sdk.socket_mode.request import SocketModeRequest from slack_sdk.socket_mode.response import SocketModeResponse -from slack_bolt.adapter.socket_mode.internals import build_retry_headers +from slack_bolt.adapter.socket_mode.internals import build_headers from slack_bolt.app.async_app import AsyncApp from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse async def run_async_bolt_app(app: AsyncApp, req: SocketModeRequest): - bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload, headers=build_retry_headers(req)) + bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload, headers=build_headers(req)) bolt_resp: BoltResponse = await app.async_dispatch(bolt_req) return bolt_resp diff --git a/slack_bolt/adapter/socket_mode/internals.py b/slack_bolt/adapter/socket_mode/internals.py index 9d6c3f898..6289f28f5 100644 --- a/slack_bolt/adapter/socket_mode/internals.py +++ b/slack_bolt/adapter/socket_mode/internals.py @@ -14,7 +14,7 @@ from slack_bolt.response import BoltResponse -def build_retry_headers(req: SocketModeRequest) -> Optional[Dict[str, Union[str, Sequence[str]]]]: +def build_headers(req: SocketModeRequest) -> Optional[Dict[str, Union[str, Sequence[str]]]]: # Mirror the HTTP mode retry headers so middleware/listeners can detect Events API retries headers: Dict[str, Union[str, Sequence[str]]] = {} if req.retry_attempt is not None: @@ -25,7 +25,7 @@ def build_retry_headers(req: SocketModeRequest) -> Optional[Dict[str, Union[str, def run_bolt_app(app: App, req: SocketModeRequest): - bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload, headers=build_retry_headers(req)) + bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload, headers=build_headers(req)) bolt_resp: BoltResponse = app.dispatch(bolt_req) return bolt_resp diff --git a/tests/adapter_tests/socket_mode/test_interactions_builtin.py b/tests/adapter_tests/socket_mode/test_interactions_builtin.py index 2ecd52554..aec04d938 100644 --- a/tests/adapter_tests/socket_mode/test_interactions_builtin.py +++ b/tests/adapter_tests/socket_mode/test_interactions_builtin.py @@ -36,7 +36,7 @@ def teardown_method(self): def test_interactions(self): app = App(client=self.web_client) - result = {"shortcut": False, "command": False} + result = {"shortcut": False, "command": False, "message": False} @app.shortcut("do-something") def shortcut_handler(ack): @@ -48,6 +48,13 @@ def command_handler(ack): result["command"] = True ack() + @app.message("<@W111>") + def message_handler(ack, req): + result["message"] = req.headers.get("x-slack-retry-num") == ["1"] and req.headers.get( + "x-slack-retry-reason" + ) == ["timeout"] + ack() + handler = SocketModeHandler( app_token="xapp-A111-222-xyz", app=app, @@ -66,5 +73,6 @@ def command_handler(ack): time.sleep(2) assert result["shortcut"] is True assert result["command"] is True + assert result["message"] is True finally: handler.client.close() diff --git a/tests/adapter_tests/socket_mode/test_interactions_websocket_client.py b/tests/adapter_tests/socket_mode/test_interactions_websocket_client.py index ccaa89d3e..90e027f9d 100644 --- a/tests/adapter_tests/socket_mode/test_interactions_websocket_client.py +++ b/tests/adapter_tests/socket_mode/test_interactions_websocket_client.py @@ -37,7 +37,7 @@ def test_interactions(self): app = App(client=self.web_client) - result = {"shortcut": False, "command": False} + result = {"shortcut": False, "command": False, "message": False} @app.shortcut("do-something") def shortcut_handler(ack): @@ -49,6 +49,13 @@ def command_handler(ack): result["command"] = True ack() + @app.message("<@W111>") + def message_handler(ack, req): + result["message"] = req.headers.get("x-slack-retry-num") == ["1"] and req.headers.get( + "x-slack-retry-reason" + ) == ["timeout"] + ack() + handler = SocketModeHandler( app_token="xapp-A111-222-xyz", app=app, @@ -67,5 +74,6 @@ def command_handler(ack): time.sleep(2) assert result["shortcut"] is True assert result["command"] is True + assert result["message"] is True finally: handler.client.close() diff --git a/tests/adapter_tests/socket_mode/test_internals.py b/tests/adapter_tests/socket_mode/test_internals.py index 2289196d4..fede30b48 100644 --- a/tests/adapter_tests/socket_mode/test_internals.py +++ b/tests/adapter_tests/socket_mode/test_internals.py @@ -1,12 +1,12 @@ from slack_sdk.socket_mode.request import SocketModeRequest -from slack_bolt.adapter.socket_mode.internals import build_retry_headers, run_bolt_app +from slack_bolt.adapter.socket_mode.internals import build_headers, run_bolt_app class TestSocketModeInternals: def test_build_retry_headers_without_retry(self): req = SocketModeRequest(type="events_api", envelope_id="e1", payload={"type": "event_callback"}) - assert build_retry_headers(req) is None + assert build_headers(req) is None def test_build_retry_headers_with_retry(self): req = SocketModeRequest( @@ -16,24 +16,5 @@ def test_build_retry_headers_with_retry(self): retry_attempt=2, retry_reason="http_timeout", ) - headers = build_retry_headers(req) + headers = build_headers(req) assert headers == {"x-slack-retry-num": "2", "x-slack-retry-reason": "http_timeout"} - - def test_run_bolt_app_propagates_retry_headers(self): - captured = {} - - class FakeApp: - def dispatch(self, bolt_req): - captured["headers"] = bolt_req.headers - return None - - req = SocketModeRequest( - type="events_api", - envelope_id="e1", - payload={"type": "event_callback", "event": {"type": "app_mention"}}, - retry_attempt=1, - retry_reason="http_timeout", - ) - run_bolt_app(FakeApp(), req) - assert captured["headers"]["x-slack-retry-num"] == ["1"] - assert captured["headers"]["x-slack-retry-reason"] == ["http_timeout"] diff --git a/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py b/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py index e8077f10c..812806a8c 100644 --- a/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py +++ b/tests/adapter_tests_async/socket_mode/test_async_aiohttp.py @@ -40,7 +40,7 @@ async def test_events(self): app = AsyncApp(client=self.web_client) - result = {"shortcut": False, "command": False} + result = {"shortcut": False, "command": False, "message": False} @app.shortcut("do-something") async def shortcut_handler(ack): @@ -52,6 +52,13 @@ async def command_handler(ack): result["command"] = True await ack() + @app.message("<@W111>") + async def message_handler(ack, req): + result["message"] = req.headers.get("x-slack-retry-num") == ["1"] and req.headers.get( + "x-slack-retry-reason" + ) == ["timeout"] + await ack() + handler = AsyncSocketModeHandler( app_token="xapp-A111-222-xyz", app=app, @@ -67,6 +74,7 @@ async def command_handler(ack): await asyncio.sleep(2) assert result["shortcut"] is True assert result["command"] is True + assert result["message"] is True finally: await handler.client.close() stop_socket_mode_server(self) diff --git a/tests/adapter_tests_async/socket_mode/test_async_websockets.py b/tests/adapter_tests_async/socket_mode/test_async_websockets.py index 84d20b2f9..fc27150ee 100644 --- a/tests/adapter_tests_async/socket_mode/test_async_websockets.py +++ b/tests/adapter_tests_async/socket_mode/test_async_websockets.py @@ -40,7 +40,7 @@ async def test_events(self): app = AsyncApp(client=self.web_client) - result = {"shortcut": False, "command": False} + result = {"shortcut": False, "command": False, "message": False} @app.shortcut("do-something") async def shortcut_handler(ack): @@ -52,6 +52,13 @@ async def command_handler(ack): result["command"] = True await ack() + @app.message("<@W111>") + async def message_handler(ack, req): + result["message"] = req.headers.get("x-slack-retry-num") == ["1"] and req.headers.get( + "x-slack-retry-reason" + ) == ["timeout"] + await ack() + handler = AsyncSocketModeHandler( app_token="xapp-A111-222-xyz", app=app, @@ -67,6 +74,7 @@ async def command_handler(ack): await asyncio.sleep(2) assert result["shortcut"] is True assert result["command"] is True + assert result["message"] is True finally: await handler.client.close() stop_socket_mode_server(self) From 331933075abb506a6a05ad22db64618a8ede9999 Mon Sep 17 00:00:00 2001 From: Ismail Pelaseyed Date: Fri, 15 May 2026 18:55:37 +0200 Subject: [PATCH 227/282] fix: Require signatures for ssl_check request verification (#1495) Co-authored-by: William Bergamin --- .../request_verification.py | 2 +- tests/adapter_tests/wsgi/test_wsgi_http.py | 41 +++++++++++++++++++ tests/scenario_tests/test_slash_command.py | 28 +++++++++++++ .../test_slash_command.py | 29 +++++++++++++ .../test_request_verification.py | 15 +++++++ .../test_request_verification.py | 16 ++++++++ 6 files changed, 130 insertions(+), 1 deletion(-) diff --git a/slack_bolt/middleware/request_verification/request_verification.py b/slack_bolt/middleware/request_verification/request_verification.py index 2cf7e361e..c0f3f5c31 100644 --- a/slack_bolt/middleware/request_verification/request_verification.py +++ b/slack_bolt/middleware/request_verification/request_verification.py @@ -49,7 +49,7 @@ def process( @staticmethod def _can_skip(mode: str, body: Dict[str, Any]) -> bool: - return mode == "socket_mode" or (body is not None and body.get("ssl_check") == "1") + return mode == "socket_mode" @staticmethod def _build_error_response() -> BoltResponse: diff --git a/tests/adapter_tests/wsgi/test_wsgi_http.py b/tests/adapter_tests/wsgi/test_wsgi_http.py index 63ac62627..c7fca2e25 100644 --- a/tests/adapter_tests/wsgi/test_wsgi_http.py +++ b/tests/adapter_tests/wsgi/test_wsgi_http.py @@ -89,6 +89,47 @@ def command_handler(ack): assert response.headers.get("content-type") == "text/plain;charset=utf-8" assert_auth_test_count(self, 1) + def test_ssl_check_param_does_not_bypass_request_verification(self): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ssl_check_enabled=False, + ) + command_called = False + + def command_handler(ack): + nonlocal command_called + command_called = True + ack() + + app.command("/hello-world")(command_handler) + + body = ( + "token=verification_token" + "&team_id=T111" + "&team_domain=test-domain" + "&channel_id=C111" + "&channel_name=random" + "&user_id=W111" + "&user_name=primary-owner" + "&command=%2Fhello-world" + "&text=Hi" + "&enterprise_id=E111" + "&enterprise_name=Org+Name" + "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx" + "&trigger_id=111.111.xxx" + "&ssl_check=1" + ) + headers = self.build_raw_headers("0", body) + headers["x-slack-signature"] = "v0=invalid" + + wsgi_server = WsgiTestServer(SlackRequestHandler(app)) + response = wsgi_server.http(method="POST", headers=headers, body=body) + + assert response.status == "401 Unauthorized" + assert response.body == """{"error": "invalid request"}""" + assert command_called is False + def test_events(self): app = App( client=self.web_client, diff --git a/tests/scenario_tests/test_slash_command.py b/tests/scenario_tests/test_slash_command.py index 1db13ecca..ae7e7c53b 100644 --- a/tests/scenario_tests/test_slash_command.py +++ b/tests/scenario_tests/test_slash_command.py @@ -93,6 +93,34 @@ def test_failure(self): assert response.status == 404 assert_auth_test_count(self, 1) + def test_ssl_check_param_does_not_bypass_request_verification(self): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ssl_check_enabled=False, + ) + command_called = False + + def command_handler(ack): + nonlocal command_called + command_called = True + ack() + + app.command("/hello-world")(command_handler) + + request = BoltRequest( + body=f"{slash_command_body}&ssl_check=1", + headers={ + "content-type": ["application/x-www-form-urlencoded"], + "x-slack-signature": ["v0=invalid"], + "x-slack-request-timestamp": ["0"], + }, + ) + response = app.dispatch(request) + assert response.status == 401 + assert response.body == """{"error": "invalid request"}""" + assert command_called is False + slash_command_body = ( "token=verification_token" diff --git a/tests/scenario_tests_async/test_slash_command.py b/tests/scenario_tests_async/test_slash_command.py index 1ac02bce7..918ccba87 100644 --- a/tests/scenario_tests_async/test_slash_command.py +++ b/tests/scenario_tests_async/test_slash_command.py @@ -100,6 +100,35 @@ async def test_failure(self): assert response.status == 404 await assert_auth_test_count_async(self, 1) + @pytest.mark.asyncio + async def test_ssl_check_param_does_not_bypass_request_verification(self): + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + ssl_check_enabled=False, + ) + command_called = False + + async def command_handler(ack): + nonlocal command_called + command_called = True + await ack() + + app.command("/hello-world")(command_handler) + + request = AsyncBoltRequest( + body=f"{slash_command_body}&ssl_check=1", + headers={ + "content-type": ["application/x-www-form-urlencoded"], + "x-slack-signature": ["v0=invalid"], + "x-slack-request-timestamp": ["0"], + }, + ) + response = await app.async_dispatch(request) + assert response.status == 401 + assert response.body == """{"error": "invalid request"}""" + assert command_called is False + slash_command_body = ( "token=verification_token" diff --git a/tests/slack_bolt/middleware/request_verification/test_request_verification.py b/tests/slack_bolt/middleware/request_verification/test_request_verification.py index 2c9adea43..ae163a84d 100644 --- a/tests/slack_bolt/middleware/request_verification/test_request_verification.py +++ b/tests/slack_bolt/middleware/request_verification/test_request_verification.py @@ -45,3 +45,18 @@ def test_invalid(self): resp = middleware.process(req=req, resp=resp, next=next) assert resp.status == 401 assert resp.body == """{"error": "invalid request"}""" + + def test_ssl_check_param_requires_valid_signature(self): + middleware = RequestVerification(signing_secret=self.signing_secret) + req = BoltRequest( + body="token=random&ssl_check=1", + headers={ + "content-type": ["application/x-www-form-urlencoded"], + "x-slack-signature": ["v0=invalid"], + "x-slack-request-timestamp": ["0"], + }, + ) + resp = BoltResponse(status=404) + resp = middleware.process(req=req, resp=resp, next=next) + assert resp.status == 401 + assert resp.body == """{"error": "invalid request"}""" diff --git a/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py b/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py index c097dd146..28921bc87 100644 --- a/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py +++ b/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py @@ -50,3 +50,19 @@ async def test_invalid(self): resp = await middleware.async_process(req=req, resp=resp, next=next) assert resp.status == 401 assert resp.body == """{"error": "invalid request"}""" + + @pytest.mark.asyncio + async def test_ssl_check_param_requires_valid_signature(self): + middleware = AsyncRequestVerification(signing_secret="secret") + req = AsyncBoltRequest( + body="token=random&ssl_check=1", + headers={ + "content-type": ["application/x-www-form-urlencoded"], + "x-slack-signature": ["v0=invalid"], + "x-slack-request-timestamp": ["0"], + }, + ) + resp = BoltResponse(status=404) + resp = await middleware.async_process(req=req, resp=resp, next=next) + assert resp.status == 401 + assert resp.body == """{"error": "invalid request"}""" From f3eeefe05da6dfdbb2381df2cc137eebc12c9852 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 20 May 2026 10:42:43 -0700 Subject: [PATCH 228/282] feat: add authorship arguments to say_stream (#1502) Co-authored-by: William Bergamin --- .../context/say_stream/async_say_stream.py | 9 ++ slack_bolt/context/say_stream/say_stream.py | 9 ++ .../context/set_status/async_set_status.py | 6 + slack_bolt/context/set_status/set_status.py | 6 + tests/slack_bolt/context/test_say_stream.py | 107 ++++++++------- tests/slack_bolt/context/test_set_status.py | 9 ++ .../context/test_async_say_stream.py | 125 ++++++++++++------ .../context/test_async_set_status.py | 10 ++ 8 files changed, 192 insertions(+), 89 deletions(-) diff --git a/slack_bolt/context/say_stream/async_say_stream.py b/slack_bolt/context/say_stream/async_say_stream.py index af776891b..df9b362e2 100644 --- a/slack_bolt/context/say_stream/async_say_stream.py +++ b/slack_bolt/context/say_stream/async_say_stream.py @@ -34,6 +34,9 @@ async def __call__( recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncChatStream: """Starts a new chat stream with context.""" @@ -51,6 +54,9 @@ async def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return await self.client.chat_stream( @@ -58,5 +64,8 @@ async def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/slack_bolt/context/say_stream/say_stream.py b/slack_bolt/context/say_stream/say_stream.py index b6a5ca797..15bdcc110 100644 --- a/slack_bolt/context/say_stream/say_stream.py +++ b/slack_bolt/context/say_stream/say_stream.py @@ -34,6 +34,9 @@ def __call__( recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> ChatStream: """Starts a new chat stream with context.""" @@ -51,6 +54,9 @@ def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return self.client.chat_stream( @@ -58,5 +64,8 @@ def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/slack_bolt/context/set_status/async_set_status.py b/slack_bolt/context/set_status/async_set_status.py index e2c451f46..f10cc195c 100644 --- a/slack_bolt/context/set_status/async_set_status.py +++ b/slack_bolt/context/set_status/async_set_status.py @@ -23,6 +23,9 @@ async def __call__( self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncSlackResponse: return await self.client.assistant_threads_setStatus( @@ -30,5 +33,8 @@ async def __call__( thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/slack_bolt/context/set_status/set_status.py b/slack_bolt/context/set_status/set_status.py index 0ed612e16..055a5cab7 100644 --- a/slack_bolt/context/set_status/set_status.py +++ b/slack_bolt/context/set_status/set_status.py @@ -23,6 +23,9 @@ def __call__( self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> SlackResponse: return self.client.assistant_threads_setStatus( @@ -30,5 +33,8 @@ def __call__( thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/tests/slack_bolt/context/test_say_stream.py b/tests/slack_bolt/context/test_say_stream.py index 29d244a65..04a52e419 100644 --- a/tests/slack_bolt/context/test_say_stream.py +++ b/tests/slack_bolt/context/test_say_stream.py @@ -1,21 +1,14 @@ import pytest +from unittest.mock import patch, MagicMock + from slack_sdk import WebClient from slack_bolt.context.say_stream.say_stream import SayStream -from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server class TestSayStream: - default_chat_stream_buffer_size = WebClient.chat_stream.__kwdefaults__["buffer_size"] - def setup_method(self): - setup_mock_web_api_server(self) - valid_token = "xoxb-valid" - mock_api_server_base_url = "http://localhost:8888" - self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url) - - def teardown_method(self): - cleanup_mock_web_api_server(self) + self.web_client = WebClient(token="xoxb-valid") def test_missing_channel_raises(self): say_stream = SayStream(client=self.web_client, channel=None, thread_ts="111.222") @@ -35,16 +28,17 @@ def test_default_params(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = say_stream() - - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C111", - "thread_ts": "111.222", - "recipient_team_id": "T111", - "recipient_user_id": "U111", - "task_display_mode": None, - } + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream() + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=None, + icon_url=None, + username=None, + ) def test_parameter_overrides(self): say_stream = SayStream( @@ -54,16 +48,17 @@ def test_parameter_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") - - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - } + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") + mock_chat_stream.assert_called_once_with( + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) def test_buffer_size_overrides(self): say_stream = SayStream( @@ -73,19 +68,41 @@ def test_buffer_size_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = say_stream( - buffer_size=100, - channel="C222", - thread_ts="333.444", - recipient_team_id="T222", - recipient_user_id="U222", - ) + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream( + buffer_size=100, + channel="C222", + thread_ts="333.444", + recipient_team_id="T222", + recipient_user_id="U222", + ) + mock_chat_stream.assert_called_once_with( + buffer_size=100, + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) - assert stream._buffer_size == 100 - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - } + def test_authorship_overrides(self): + say_stream = SayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=":maple_leaf:", + icon_url=None, + username="Charlie Brown", + ) diff --git a/tests/slack_bolt/context/test_set_status.py b/tests/slack_bolt/context/test_set_status.py index fe998df5e..bb5807e96 100644 --- a/tests/slack_bolt/context/test_set_status.py +++ b/tests/slack_bolt/context/test_set_status.py @@ -32,6 +32,15 @@ def test_set_status_loading_messages(self): ) assert response.status_code == 200 + def test_set_status_authorship(self): + set_status = SetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: SlackResponse = set_status( + status="Thinking...", + icon_emoji=":maple_leaf:", + username="Charlie Brown", + ) + assert response.status_code == 200 + def test_set_status_invalid(self): set_status = SetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") with pytest.raises(TypeError): diff --git a/tests/slack_bolt_async/context/test_async_say_stream.py b/tests/slack_bolt_async/context/test_async_say_stream.py index 016549bd6..7ac084044 100644 --- a/tests/slack_bolt_async/context/test_async_say_stream.py +++ b/tests/slack_bolt_async/context/test_async_say_stream.py @@ -1,28 +1,20 @@ import pytest +from unittest.mock import patch, MagicMock + from slack_sdk.web.async_client import AsyncWebClient from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream -from tests.mock_web_api_server import ( - cleanup_mock_web_api_server, - setup_mock_web_api_server, -) from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncSayStream: - default_chat_stream_buffer_size = AsyncWebClient.chat_stream.__kwdefaults__["buffer_size"] - @pytest.fixture(scope="function", autouse=True) def setup_teardown(self): old_os_env = remove_os_env_temporarily() - setup_mock_web_api_server(self) - valid_token = "xoxb-valid" - mock_api_server_base_url = "http://localhost:8888" try: - self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - yield # run the test here + self.web_client = AsyncWebClient(token="xoxb-valid") + yield finally: - cleanup_mock_web_api_server(self) restore_os_env(old_os_env) @pytest.mark.asyncio @@ -46,16 +38,22 @@ async def test_default_params(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = await say_stream() + mock_chat_stream = MagicMock() - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C111", - "thread_ts": "111.222", - "recipient_team_id": "T111", - "recipient_user_id": "U111", - "task_display_mode": None, - } + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) + + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream() + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=None, + icon_url=None, + username=None, + ) @pytest.mark.asyncio async def test_parameter_overrides(self): @@ -66,16 +64,22 @@ async def test_parameter_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = await say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") + mock_chat_stream = MagicMock() - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - } + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) + + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") + mock_chat_stream.assert_called_once_with( + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) @pytest.mark.asyncio async def test_buffer_size_overrides(self): @@ -86,19 +90,52 @@ async def test_buffer_size_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = await say_stream( - buffer_size=100, - channel="C222", - thread_ts="333.444", - recipient_team_id="T222", - recipient_user_id="U222", + mock_chat_stream = MagicMock() + + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) + + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream( + buffer_size=100, + channel="C222", + thread_ts="333.444", + recipient_team_id="T222", + recipient_user_id="U222", + ) + mock_chat_stream.assert_called_once_with( + buffer_size=100, + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) + + @pytest.mark.asyncio + async def test_authorship_overrides(self): + say_stream = AsyncSayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", ) + mock_chat_stream = MagicMock() + + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) - assert stream._buffer_size == 100 - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - } + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=":maple_leaf:", + icon_url=None, + username="Charlie Brown", + ) diff --git a/tests/slack_bolt_async/context/test_async_set_status.py b/tests/slack_bolt_async/context/test_async_set_status.py index e785ff89e..bcf1fcf19 100644 --- a/tests/slack_bolt_async/context/test_async_set_status.py +++ b/tests/slack_bolt_async/context/test_async_set_status.py @@ -40,6 +40,16 @@ async def test_set_status_loading_messages(self): ) assert response.status_code == 200 + @pytest.mark.asyncio + async def test_set_status_authorship(self): + set_status = AsyncSetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: AsyncSlackResponse = await set_status( + status="Thinking...", + icon_emoji=":maple_leaf:", + username="Charlie Brown", + ) + assert response.status_code == 200 + @pytest.mark.asyncio async def test_set_status_invalid(self): set_status = AsyncSetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") From da81ecc240846df6cfe997fe39d55f60a8df6365 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 14:08:31 -0700 Subject: [PATCH 229/282] chore(deps): update starlette requirement from <1,>=0.19.1 to >=0.49.3,<1 (#1493) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin --- requirements/adapter.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 2564aae79..091210722 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -23,7 +23,8 @@ tracerite<1.1.2; python_version<="3.8" # older versions of python are not compa sanic>=21,<24; python_version<="3.8" sanic>=21,<26; python_version>"3.8" -starlette>=0.19.1,<1 +starlette>=0.19.1,<0.45; python_version<"3.9" +starlette>=0.49.3,<1; python_version>="3.9" tornado>=6,<7 uvicorn<1 # The oldest version can vary among Python runtime versions gunicorn>=20,<24 From a51cefd618f90bb71070bbce2134902dd1731a78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 10:57:13 -0700 Subject: [PATCH 230/282] chore(deps): update gunicorn requirement from <24,>=20 to >=23.0.0,<24 (#1504) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/adapter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 091210722..002b81b0a 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -27,5 +27,5 @@ starlette>=0.19.1,<0.45; python_version<"3.9" starlette>=0.49.3,<1; python_version>="3.9" tornado>=6,<7 uvicorn<1 # The oldest version can vary among Python runtime versions -gunicorn>=20,<24 +gunicorn>=23.0.0,<24 websocket_client>=1.2.3,<2 # Socket Mode 3rd party implementation From bddebcad84761cd00ba44d662902e52a79c5f18c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 18:02:10 +0000 Subject: [PATCH 231/282] chore(deps): update sanic requirement from <26,>=21 to >=25.3.0,<26 (#1506) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/adapter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index 002b81b0a..ed3cf5b82 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -21,7 +21,7 @@ setuptools<82 # Pinned: Pyramid depends on pkg_resources (deprecated in setupto # Note: Sanic imports tracerite with wild card versions tracerite<1.1.2; python_version<="3.8" # older versions of python are not compatible with tracerite>1.1.2 sanic>=21,<24; python_version<="3.8" -sanic>=21,<26; python_version>"3.8" +sanic>=25.3.0,<26; python_version>"3.8" starlette>=0.19.1,<0.45; python_version<"3.9" starlette>=0.49.3,<1; python_version>="3.9" From 87d0897f90f6a8766a5aa20cd787aa7cd5fcc860 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 27 May 2026 12:45:41 -0400 Subject: [PATCH 232/282] fix: broken python 3.10 unit test (#1509) --- .../adapter_tests_async/test_async_falcon.py | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/tests/adapter_tests_async/test_async_falcon.py b/tests/adapter_tests_async/test_async_falcon.py index 6e3901fdf..5602ed6fb 100644 --- a/tests/adapter_tests_async/test_async_falcon.py +++ b/tests/adapter_tests_async/test_async_falcon.py @@ -2,8 +2,9 @@ from time import time from urllib.parse import quote +import pytest import falcon -from falcon import testing +from falcon.testing import ASGIConductor from slack_sdk.signature import SignatureVerifier from slack_sdk.web.async_client import AsyncWebClient @@ -55,7 +56,8 @@ def build_headers(self, timestamp: str, body: str): "x-slack-request-timestamp": timestamp, } - def test_events(self): + @pytest.mark.asyncio + async def test_events(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, @@ -92,16 +94,17 @@ async def event_handler(): resource = AsyncSlackAppResource(app) api.add_route("/slack/events", resource) - client = testing.TestClient(api) - response = client.simulate_post( - "/slack/events", - body=body, - headers=self.build_headers(timestamp, body), - ) + async with ASGIConductor(api) as conductor: + response = await conductor.simulate_post( + "/slack/events", + body=body, + headers=self.build_headers(timestamp, body), + ) assert response.status_code == 200 assert_auth_test_count(self, 1) - def test_shortcuts(self): + @pytest.mark.asyncio + async def test_shortcuts(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, @@ -133,16 +136,17 @@ async def shortcut_handler(ack): resource = AsyncSlackAppResource(app) api.add_route("/slack/events", resource) - client = testing.TestClient(api) - response = client.simulate_post( - "/slack/events", - body=body, - headers=self.build_headers(timestamp, body), - ) + async with ASGIConductor(api) as conductor: + response = await conductor.simulate_post( + "/slack/events", + body=body, + headers=self.build_headers(timestamp, body), + ) assert response.status_code == 200 assert_auth_test_count(self, 1) - def test_commands(self): + @pytest.mark.asyncio + async def test_commands(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, @@ -174,16 +178,17 @@ async def command_handler(ack): resource = AsyncSlackAppResource(app) api.add_route("/slack/events", resource) - client = testing.TestClient(api) - response = client.simulate_post( - "/slack/events", - body=body, - headers=self.build_headers(timestamp, body), - ) + async with ASGIConductor(api) as conductor: + response = await conductor.simulate_post( + "/slack/events", + body=body, + headers=self.build_headers(timestamp, body), + ) assert response.status_code == 200 assert_auth_test_count(self, 1) - def test_oauth(self): + @pytest.mark.asyncio + async def test_oauth(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, @@ -197,8 +202,8 @@ def test_oauth(self): resource = AsyncSlackAppResource(app) api.add_route("/slack/install", resource) - client = testing.TestClient(api) - response = client.simulate_get("/slack/install") + async with ASGIConductor(api) as conductor: + response = await conductor.simulate_get("/slack/install") assert response.status_code == 200 assert response.headers.get("content-type") == "text/html; charset=utf-8" assert response.headers.get("content-length") == "607" From 757283ec4bdcaec90953e444b8dce5fe2f63e809 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 16:49:13 +0000 Subject: [PATCH 233/282] chore(deps): update django requirement from <6,>=3 to >=4.2.30,<6 (#1505) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter.txt b/requirements/adapter.txt index ed3cf5b82..5e51d9589 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter.txt @@ -7,7 +7,8 @@ chalice>=1.28,<1.31; python_version<"3.9" chalice>=1.32.0,<2; python_version>="3.9" cheroot<12 CherryPy>=18,<19 -Django>=3,<6 +Django>=3.2,<4; python_version<"3.8" +Django>=4.2.30,<6; python_version>="3.8" falcon>=2,<4; python_version<"3.9" falcon>=4.2.0,<5; python_version>="3.9" fastapi>=0.70.0,<1 From ea21484e412ec8570cb44bfc3d3f3724f0f22887 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 10:24:01 -0700 Subject: [PATCH 234/282] chore(deps): update pytest-cov requirement from <8,>=3 to >=7.1.0,<8 (#1507) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin --- requirements/testing_without_asyncio.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/testing_without_asyncio.txt b/requirements/testing_without_asyncio.txt index 441b49f8b..46909c07d 100644 --- a/requirements/testing_without_asyncio.txt +++ b/requirements/testing_without_asyncio.txt @@ -1,3 +1,3 @@ # pip install -r requirements/testing_without_asyncio.txt pytest<8.5 -pytest-cov>=3,<8 +pytest-cov>=7.1.0,<8; python_version>="3.14" # only needed to evaluate coverage on the latest supported python version From 92d851613289dadf5f26d989da189e6c1bb84002 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 27 May 2026 14:53:57 -0400 Subject: [PATCH 235/282] chore: rename requirements files to improve Dependabot dependency scoping (#1510) --- .github/maintainers_guide.md | 4 ++-- .github/workflows/ci-build.yml | 22 +++++++++---------- AGENTS.md | 14 ++++++------ requirements/{adapter.txt => adapter_dev.txt} | 4 ++-- requirements/async.txt | 3 --- requirements/async_dev.txt | 3 +++ requirements/{tools.txt => dev_tools.txt} | 0 .../{testing_without_asyncio.txt => test.txt} | 2 +- .../{adapter_testing.txt => test_adapter.txt} | 2 +- requirements/test_async.txt | 4 ++++ requirements/testing.txt | 4 ---- scripts/format.sh | 2 +- scripts/generate_api_docs.sh | 4 ++-- scripts/install.sh | 8 +++---- scripts/lint.sh | 2 +- scripts/run_mypy.sh | 6 ++--- 16 files changed, 42 insertions(+), 42 deletions(-) rename requirements/{adapter.txt => adapter_dev.txt} (95%) delete mode 100644 requirements/async.txt create mode 100644 requirements/async_dev.txt rename requirements/{tools.txt => dev_tools.txt} (100%) rename requirements/{testing_without_asyncio.txt => test.txt} (69%) rename requirements/{adapter_testing.txt => test_adapter.txt} (79%) create mode 100644 requirements/test_async.txt delete mode 100644 requirements/testing.txt diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index f8edeeabd..47d9ccd58 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -71,8 +71,8 @@ If you make changes to `slack_bolt/adapter/*`, please verify if it surely works ```sh # Install all optional dependencies -$ pip install -r requirements/adapter.txt -$ pip install -r requirements/adapter_testing.txt +$ pip install -r requirements/adapter_dev.txt +$ pip install -r requirements/test_adapter.txt # Set required env variables $ export SLACK_SIGNING_SECRET=*** diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index b158c72ea..ab8cce3e2 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -48,13 +48,13 @@ jobs: run: | pip install -U pip pip install -U . - pip install -r requirements/tools.txt + pip install -r requirements/dev_tools.txt - name: Type check synchronous modules run: mypy --config-file pyproject.toml --exclude "async_|/adapter/" - name: Install async and adapter dependencies run: | - pip install -r requirements/async.txt - pip install -r requirements/adapter.txt + pip install -r requirements/async_dev.txt + pip install -r requirements/adapter_dev.txt - name: Type check all modules run: mypy --config-file pyproject.toml @@ -88,15 +88,15 @@ jobs: run: | pip install -U pip pip install . - pip install -r requirements/testing_without_asyncio.txt + pip install -r requirements/test.txt - name: Run tests without aiohttp run: | pytest tests/slack_bolt/ --junitxml=reports/test_slack_bolt.xml pytest tests/scenario_tests/ --junitxml=reports/test_scenario.xml - name: Install adapter dependencies run: | - pip install -r requirements/adapter.txt - pip install -r requirements/adapter_testing.txt + pip install -r requirements/adapter_dev.txt + pip install -r requirements/test_adapter.txt - name: Run tests for HTTP Mode adapters run: | pytest tests/adapter_tests/ \ @@ -105,14 +105,14 @@ jobs: --junitxml=reports/test_adapter.xml - name: Install async dependencies run: | - pip install -r requirements/async.txt + pip install -r requirements/async_dev.txt - name: Run tests for Socket Mode adapters run: | # Requires async test dependencies pytest tests/adapter_tests/socket_mode/ --junitxml=reports/test_adapter_socket_mode.xml - name: Install all dependencies run: | - pip install -r requirements/testing.txt + pip install -r requirements/test_async.txt - name: Run tests for HTTP Mode adapters (ASGI) run: | # Requires async test dependencies @@ -155,9 +155,9 @@ jobs: run: | pip install -U pip pip install . - pip install -r requirements/adapter.txt - pip install -r requirements/testing.txt - pip install -r requirements/adapter_testing.txt + pip install -r requirements/adapter_dev.txt + pip install -r requirements/test_async.txt + pip install -r requirements/test_adapter.txt - name: Run all tests for codecov run: | pytest --cov=./slack_bolt/ --cov-report=xml diff --git a/AGENTS.md b/AGENTS.md index 892a858e7..005f6eeda 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -183,7 +183,7 @@ Then wire it into `BoltContext` (`slack_bolt/context/context.py`) and `AsyncBolt 1. Create `slack_bolt/adapter//` 2. Add `__init__.py` and `handler.py` (or `async_handler.py` for async frameworks) 3. The handler converts the framework's request to `BoltRequest`, calls `app.dispatch()`, and converts `BoltResponse` back -4. Add the framework to `requirements/adapter.txt` with version constraints +4. Add the framework to `requirements/adapter_dev.txt` with version constraints 5. Add adapter tests in `tests/adapter_tests/` (sync) or `tests/adapter_tests_async/` (async) ### Adding a Kwargs-Injectable Argument @@ -205,12 +205,12 @@ The core package has a **single required runtime dependency**: `slack_sdk` (defi **`requirements/` directory structure:** -- `async.txt` -- async runtime deps (`aiohttp`, `websockets`) -- `adapter.txt` -- all framework adapter deps (Flask, Django, FastAPI, etc.) -- `testing.txt` -- test runner deps (`pytest`, `pytest-asyncio`, includes `async.txt`) -- `testing_without_asyncio.txt` -- test deps without async (`pytest`, `pytest-cov`) -- `adapter_testing.txt` -- adapter-specific test deps (`moto`, `boddle`, `sanic-testing`) -- `tools.txt` -- dev tools (`mypy`, `flake8`, `black`) +- `async_dev.txt` -- async runtime deps (`aiohttp`, `websockets`) +- `adapter_dev.txt` -- all framework adapter deps (Flask, Django, FastAPI, etc.) +- `test_async.txt` -- test runner deps (`pytest`, `pytest-asyncio`, includes `async_dev.txt`) +- `test.txt` -- test deps without async (`pytest`, `pytest-cov`) +- `test_adapter.txt` -- adapter-specific test deps (`moto`, `boddle`, `sanic-testing`) +- `dev_tools.txt` -- dev tools (`mypy`, `flake8`, `black`) When adding a new dependency: add it to the appropriate `requirements/*.txt` file with version constraints, never to `pyproject.toml` `dependencies` (unless it's a core runtime dep, which is very rare). diff --git a/requirements/adapter.txt b/requirements/adapter_dev.txt similarity index 95% rename from requirements/adapter.txt rename to requirements/adapter_dev.txt index 5e51d9589..7c479fd4b 100644 --- a/requirements/adapter.txt +++ b/requirements/adapter_dev.txt @@ -1,5 +1,5 @@ -# pip install -r requirements/adapter.txt -# NOTE: any of async ones requires pip install -r requirements/async.txt too +# pip install -r requirements/adapter_dev.txt +# NOTE: any of async ones requires pip install -r requirements/async_dev.txt too # used only under slack_bolt/adapter boto3<=2 bottle>=0.12,<1 diff --git a/requirements/async.txt b/requirements/async.txt deleted file mode 100644 index af3e49913..000000000 --- a/requirements/async.txt +++ /dev/null @@ -1,3 +0,0 @@ -# pip install -r requirements/async.txt -aiohttp>=3,<4 -websockets<16 diff --git a/requirements/async_dev.txt b/requirements/async_dev.txt new file mode 100644 index 000000000..c9641cc6f --- /dev/null +++ b/requirements/async_dev.txt @@ -0,0 +1,3 @@ +# pip install -r requirements/async_dev.txt +aiohttp>=3,<4 +websockets<16 diff --git a/requirements/tools.txt b/requirements/dev_tools.txt similarity index 100% rename from requirements/tools.txt rename to requirements/dev_tools.txt diff --git a/requirements/testing_without_asyncio.txt b/requirements/test.txt similarity index 69% rename from requirements/testing_without_asyncio.txt rename to requirements/test.txt index 46909c07d..e007e6637 100644 --- a/requirements/testing_without_asyncio.txt +++ b/requirements/test.txt @@ -1,3 +1,3 @@ -# pip install -r requirements/testing_without_asyncio.txt +# pip install -r requirements/test.txt pytest<8.5 pytest-cov>=7.1.0,<8; python_version>="3.14" # only needed to evaluate coverage on the latest supported python version diff --git a/requirements/adapter_testing.txt b/requirements/test_adapter.txt similarity index 79% rename from requirements/adapter_testing.txt rename to requirements/test_adapter.txt index dd4a1cf84..cb81cd057 100644 --- a/requirements/adapter_testing.txt +++ b/requirements/test_adapter.txt @@ -1,4 +1,4 @@ -# pip install -r requirements/adapter_testing.txt +# pip install -r requirements/test_adapter.txt moto>=3,<6 # For AWS tests docker>=5,<8 # Used by moto boddle>=0.2.9,<0.3 # For Bottle app tests diff --git a/requirements/test_async.txt b/requirements/test_async.txt new file mode 100644 index 000000000..a2533430e --- /dev/null +++ b/requirements/test_async.txt @@ -0,0 +1,4 @@ +# pip install -r requirements/test_async.txt +-r test.txt +-r async_dev.txt +pytest-asyncio<2; diff --git a/requirements/testing.txt b/requirements/testing.txt deleted file mode 100644 index 62fdcca2d..000000000 --- a/requirements/testing.txt +++ /dev/null @@ -1,4 +0,0 @@ -# pip install -r requirements/testing.txt --r testing_without_asyncio.txt --r async.txt -pytest-asyncio<2; diff --git a/scripts/format.sh b/scripts/format.sh index e73bcdac4..771cbb413 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -7,7 +7,7 @@ cd ${script_dir}/.. if [[ "$1" != "--no-install" ]]; then export PIP_REQUIRE_VIRTUALENV=1 pip install -U pip - pip install -U -r requirements/tools.txt + pip install -U -r requirements/dev_tools.txt fi black slack_bolt/ tests/ diff --git a/scripts/generate_api_docs.sh b/scripts/generate_api_docs.sh index c3b9fd260..275aa0fe1 100755 --- a/scripts/generate_api_docs.sh +++ b/scripts/generate_api_docs.sh @@ -6,8 +6,8 @@ script_dir=$(dirname "$0") cd "${script_dir}/.." pip install -U pip -pip install -U -r requirements/adapter.txt -pip install -U -r requirements/async.txt +pip install -U -r requirements/adapter_dev.txt +pip install -U -r requirements/async_dev.txt pip install -U pdoc3 pip install . rm -rf docs/reference diff --git a/scripts/install.sh b/scripts/install.sh index 96159c63c..64cdf1561 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -13,10 +13,10 @@ pip install -U pip pip uninstall python-lambda pip install -U -e . -pip install -U -r requirements/testing.txt -pip install -U -r requirements/adapter.txt -pip install -U -r requirements/adapter_testing.txt -pip install -U -r requirements/tools.txt +pip install -U -r requirements/test_async.txt +pip install -U -r requirements/adapter_dev.txt +pip install -U -r requirements/test_adapter.txt +pip install -U -r requirements/dev_tools.txt # To avoid errors due to the old versions of click forced by Chalice pip install -U pip click diff --git a/scripts/lint.sh b/scripts/lint.sh index efee01ebc..3a3037419 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -6,7 +6,7 @@ cd ${script_dir}/.. if [[ "$1" != "--no-install" ]]; then pip install -U pip - pip install -U -r requirements/tools.txt + pip install -U -r requirements/dev_tools.txt fi flake8 slack_bolt/ && flake8 examples/ diff --git a/scripts/run_mypy.sh b/scripts/run_mypy.sh index 27589b348..c9234f87a 100755 --- a/scripts/run_mypy.sh +++ b/scripts/run_mypy.sh @@ -7,9 +7,9 @@ cd ${script_dir}/.. if [[ "$1" != "--no-install" ]]; then pip install -U pip pip install -U . - pip install -U -r requirements/async.txt - pip install -U -r requirements/adapter.txt - pip install -U -r requirements/tools.txt + pip install -U -r requirements/async_dev.txt + pip install -U -r requirements/adapter_dev.txt + pip install -U -r requirements/dev_tools.txt fi mypy --config-file pyproject.toml From 886cc520488b519b5b42ab09820ade781d1cfe3e Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 29 May 2026 10:04:13 -0400 Subject: [PATCH 236/282] docs: add security policy (#1511) --- .github/SECURITY.md | 63 +++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 64 insertions(+) create mode 100644 .github/SECURITY.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 000000000..06dfdb664 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,63 @@ +# Security Policy + +Slack takes the security of its software and services seriously, including all open-source repositories managed through the [slackapi](https://github.com/slackapi) GitHub organization. + +## Reporting a Vulnerability + +**Do NOT report security vulnerabilities through public GitHub issues, pull requests, or discussions.** + +If you believe you have found a security vulnerability in `slack-bolt`, please report it through the Slack bug bounty program on HackerOne: + +**** + +Even if `slack-bolt` is not explicitly listed as an in-scope asset on the HackerOne program page, reports for vulnerabilities in this package should still be submitted there. The Slack security team triages reports for all `slackapi` open-source repositories through this program. + +If HackerOne is inaccessible, you may alternatively report the issue to [security@salesforce.com](mailto:security@salesforce.com). + +Please do not discuss potential vulnerabilities in public without first coordinating with the security team. + +## What to Include + +To help us triage and respond quickly, please include: + +- Type of vulnerability (e.g., signature bypass, token leakage, denial of service) +- Affected version(s) of `slack-bolt` +- Step-by-step reproduction instructions +- Proof-of-concept code or payloads, if available +- Impact assessment: what an attacker could achieve +- Any specific configuration required to trigger the vulnerability +- Affected source file paths, if known + +## Threat Model + +Bolt for Python is a framework that sits between the Slack platform and developer application code. Its security boundary covers the integrity and confidentiality of that interface. + +### In Scope + +The following are considered framework vulnerabilities: + +- Bypass of request signature verification (HMAC-SHA256 validation) +- OAuth token leakage or cross-tenant token exposure during authorization flows +- Denial of service caused by malformed or specially crafted payloads processed by framework internals +- Authentication or authorization bypass in any built-in adapter +- Information disclosure through framework error responses or timing side channels +- Bypass of the `ssl_check` endpoint protections + +### Out of Scope + +The following are NOT framework vulnerabilities: + +- Vulnerabilities in the Python runtime, operating system, or hosting infrastructure +- Security issues in developer application logic built on top of Bolt (e.g., SQL injection caused by passing unsanitized payload data to a database) +- Vulnerabilities in third-party PyPI packages chosen and installed by the developer outside of Bolt's direct dependencies +- Vulnerabilities in Slack's server-side platform infrastructure (report those directly under Slack's main HackerOne scope) +- Attacks that require possession of a valid signing secret or bot token +- Arbitrary attribute injection or unsafe deserialization caused by developer code handling untrusted input +- Issues that only affect end-of-life versions with no reproduction on supported versions + +## Disclosure Policy + +This project follows coordinated disclosure: + +- Allow a reasonable timeframe for the team to investigate, develop, and release a fix before any public disclosure. +- Researchers who follow responsible disclosure practices are eligible for recognition and bounty consideration through the Slack HackerOne program. diff --git a/pyproject.toml b/pyproject.toml index 88842d0d9..ac197c4f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ dependencies = ["slack_sdk>=3.38.0,<4"] [project.urls] Documentation = "https://docs.slack.dev/tools/bolt-python/" +Source = "https://github.com/slackapi/bolt-python" [tool.setuptools.packages.find] include = ["slack_bolt*"] From df29f83127e9a2cb02ec164889fc47b619979b8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 07:52:17 -0700 Subject: [PATCH 237/282] chore(deps-dev): update tornado requirement from <7,>=6 to >=6.5.6,<7 (#1508) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 7c479fd4b..ea924f5bb 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -26,7 +26,8 @@ sanic>=25.3.0,<26; python_version>"3.8" starlette>=0.19.1,<0.45; python_version<"3.9" starlette>=0.49.3,<1; python_version>="3.9" -tornado>=6,<7 +tornado>=6.2,<7; python_version<"3.9" +tornado>=6.5.6,<7; python_version>="3.9" uvicorn<1 # The oldest version can vary among Python runtime versions gunicorn>=23.0.0,<24 websocket_client>=1.2.3,<2 # Socket Mode 3rd party implementation From e8222b590bed889b2bee356fcf102e9e1bab2756 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Fri, 29 May 2026 08:40:01 -0700 Subject: [PATCH 238/282] docs: fix critical inaccuracies in English documentation (#1512) Co-authored-by: Claude --- .../english/concepts/adding-agent-features.md | 6 ++--- docs/english/concepts/message-sending.md | 4 ++-- .../concepts/updating-pushing-views.md | 2 +- .../concepts/using-the-assistant-class.md | 8 +++---- docs/english/concepts/view-submissions.md | 2 +- docs/english/experiments.md | 23 ++----------------- .../custom-steps-workflow-builder-existing.md | 5 ++-- .../custom-steps-workflow-builder-new.md | 8 ++++--- docs/english/tutorial/custom-steps.md | 12 +++++----- 9 files changed, 26 insertions(+), 44 deletions(-) diff --git a/docs/english/concepts/adding-agent-features.md b/docs/english/concepts/adding-agent-features.md index cbd164630..87a8d31cb 100644 --- a/docs/english/concepts/adding-agent-features.md +++ b/docs/english/concepts/adding-agent-features.md @@ -190,7 +190,7 @@ def handle_message( # Add eyes reaction only to the first message (DMs only — channel # threads already have the reaction from the initial app_mention) if is_dm and not existing_session_id: - await client.reactions_add( + client.reactions_add( channel=channel_id, timestamp=event["ts"], name="eyes", @@ -274,7 +274,7 @@ The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient. | `recipient_team_id` | Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). | `recipient_user_id` | Sourced from the `user_id` of the event. -If neither a `channel_id` or `thread_ts` can be sourced, then the utility will be `None`. +If either `channel_id` or `thread_ts` cannot be sourced, the utility will be `None`. ```python streamer = say_stream() @@ -571,7 +571,7 @@ def handle_app_mentioned( except Exception as e: logger.exception(f"Failed to handle app mention: {e}") - await say( + say( text=f":warning: Something went wrong! ({e})", thread_ts=event.get("thread_ts") or event["ts"], ) diff --git a/docs/english/concepts/message-sending.md b/docs/english/concepts/message-sending.md index 090503ff2..c4d1b0467 100644 --- a/docs/english/concepts/message-sending.md +++ b/docs/english/concepts/message-sending.md @@ -54,7 +54,7 @@ The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient. | `recipient_team_id` | Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). | `recipient_user_id` | Sourced from the `user_id` of the event. -If neither a `channel_id` or `thread_ts` can be sourced, then the utility will be `None`. +If either `channel_id` or `thread_ts` cannot be sourced, the utility will be `None`. For information on calling the `chat_*Stream` API methods directly, see the [_Sending streaming messages_](/tools/python-slack-sdk/web#sending-streaming-messages) section of the Python Slack SDK docs. @@ -79,7 +79,7 @@ def handle_app_mention(client: WebClient, say_stream: SayStream): def handle_message(client: WebClient, say_stream: SayStream): stream = say_stream() - stream.append(markdown_text="Let me consult my *vast knowledge database*...) + stream.append(markdown_text="Let me consult my *vast knowledge database*...") stream.stop() if __name__ == "__main__": diff --git a/docs/english/concepts/updating-pushing-views.md b/docs/english/concepts/updating-pushing-views.md index 8c05e79c8..ce285c814 100644 --- a/docs/english/concepts/updating-pushing-views.md +++ b/docs/english/concepts/updating-pushing-views.md @@ -1,6 +1,6 @@ # Updating & pushing views -Modals contain a stack of views. When you call [`views_open`](https://api./reference/methods/views.open/slack.com/methods/views.open), you add the root view to the modal. After the initial call, you can dynamically update a view by calling [`views_update`](/reference/methods/views.update/), or stack a new view on top of the root view by calling [`views_push`](/reference/methods/views.push/) +Modals contain a stack of views. When you call [`views_open`](/reference/methods/views.open/), you add the root view to the modal. After the initial call, you can dynamically update a view by calling [`views_update`](/reference/methods/views.update/), or stack a new view on top of the root view by calling [`views_push`](/reference/methods/views.push/) ## The `views_update` method diff --git a/docs/english/concepts/using-the-assistant-class.md b/docs/english/concepts/using-the-assistant-class.md index ed004dc35..40c97d0cd 100644 --- a/docs/english/concepts/using-the-assistant-class.md +++ b/docs/english/concepts/using-the-assistant-class.md @@ -51,7 +51,7 @@ You _could_ go it alone and [listen](/tools/bolt-python/concepts/event-listening While the `assistant_thread_started` and `assistant_thread_context_changed` events do provide Slack-client thread context information, the `message.im` event does not. Any subsequent user message events won't contain thread context data. For that reason, Bolt not only provides a way to store thread context — the `threadContextStore` property — but it also provides a `DefaultThreadContextStore` instance that is utilized by default. This implementation relies on storing and retrieving [message metadata](/messaging/message-metadata/) as the user interacts with the app. -If you do provide your own `threadContextStore` property, it must feature `get` and `save` methods. +If you do provide your own `threadContextStore` property, it must feature `find` and `save` methods. :::tip[Refer to the [reference docs](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments.] ::: @@ -138,10 +138,10 @@ Messages sent to the app do not contain a [subtype](/reference/events/message#su There are three utilities that are particularly useful in curating the user experience: * [`say`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.Say) -* [`setTitle`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetTitle) -* [`setStatus`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetStatus) +* [`set_title`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetTitle) +* [`set_status`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetStatus) -Within the `setStatus` utility, you can cycle through strings passed into a `loading_messages` array. +Within the `set_status` utility, you can cycle through strings passed into a `loading_messages` list. ```python # This listener is invoked when the human user sends a reply in the assistant thread diff --git a/docs/english/concepts/view-submissions.md b/docs/english/concepts/view-submissions.md index 4ff4c2da7..b961e0376 100644 --- a/docs/english/concepts/view-submissions.md +++ b/docs/english/concepts/view-submissions.md @@ -90,6 +90,6 @@ def handle_submission(ack, body, client, view, logger): # Message the user try: client.chat_postMessage(channel=user, text=msg) - except e: + except Exception as e: logger.exception(f"Failed to post a message {e}") ``` diff --git a/docs/english/experiments.md b/docs/english/experiments.md index 13adf0a32..443a334be 100644 --- a/docs/english/experiments.md +++ b/docs/english/experiments.md @@ -1,30 +1,11 @@ # Experiments -Bolt for Python includes experimental features still under active development. These features may be fleeting, may not be perfectly polished, and should be thought of as available for use "at your own risk." +Bolt for Python occasionally includes experimental features still under active development. These features may be fleeting, may not be perfectly polished, and should be thought of as available for use "at your own risk." Experimental features are categorized as `semver:patch` until the experimental status is removed. We love feedback from our community, so we encourage you to explore and interact with the [GitHub repo](https://github.com/slackapi/bolt-python). Contributions, bug reports, and any feedback are all helpful; let us nurture the Slack CLI together to help make building Slack apps more pleasant for everyone. ## Available experiments -* [Agent listener argument](#agent) -## Agent listener argument {#agent} - -The `agent: BoltAgent` listener argument provides access to AI agent-related features. - -The `BoltAgent` and `AsyncBoltAgent` classes offer a `chat_stream()` method that comes pre-configured with event context defaults: `channel_id`, `thread_ts`, `team_id`, and `user_id` fields. - -The listener argument is wired into the Bolt `kwargs` injection system, so listeners can declare it as a parameter or access it via the `context.agent` property. - -### Example - -```python -from slack_bolt import BoltAgent - -@app.event("app_mention") -def handle_mention(agent: BoltAgent): - stream = agent.chat_stream() - stream.append(markdown_text="Hello!") - stream.stop() -``` +There are currently no active experiments. We're steadily staying stable. diff --git a/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md b/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md index c3c5e2af7..9a5e3ee50 100644 --- a/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md +++ b/docs/english/tutorial/custom-steps-workflow-builder-existing/custom-steps-workflow-builder-existing.md @@ -14,7 +14,7 @@ In this tutorial we will: ## Prerequisites {#prereqs} -The custom steps feature is compatible with Bolt version 1.20.0 and above. First, update your `package.json` file to reflect version 1.20.0 of Bolt, then run the following command in your terminal: +The custom steps feature is compatible with Bolt version 1.20.0 and above. First, update your `requirements.txt` file to reflect version 1.20.0 of Bolt (e.g., `slack-bolt>=1.20.0`), then run the following commands in your terminal: ```sh python3 -m venv .venv @@ -215,9 +215,8 @@ def manager_resp_handler(ack: Ack, action, body: dict, client: WebClient, comple client.chat_update( channel=body['channel']['id'], - message=body['message'], ts=body["message"]["ts"], - text=f'Request {"approved" if request_decision == 'approve' else "denied"}!' + text=f"Request {'approved' if request_decision == 'approve' else 'denied'}!" ) complete({ diff --git a/docs/english/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new.md b/docs/english/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new.md index 1dceed45a..75be7aa32 100644 --- a/docs/english/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new.md +++ b/docs/english/tutorial/custom-steps-workflow-builder-new/custom-steps-workflow-builder-new.md @@ -47,7 +47,9 @@ You can also open a terminal window from inside VSCode like this: `Ctrl` + `~` Once in VSCode, open the terminal. Let's install our package dependencies: run the following command(s) in the terminal inside VSCode: ```sh -npm install +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt ``` We now have a Bolt app ready for development! Open the `manifest.json` file and copy its contents; you'll need this in the next step. @@ -99,7 +101,7 @@ You will then have a bot token. Again, copy that value and save it somewhere acc ## Starting your local development server {#local} -While building your app, you can see your changes appear in your workspace in real-time with `npm start`. Soon we'll start our local development server and see what our sample code is all about! But first, we need to store those tokens we gathered as environment variables. +While building your app, you can see your changes appear in your workspace in real-time with `python app.py`. Soon we'll start our local development server and see what our sample code is all about! But first, we need to store those tokens we gathered as environment variables. Navigate back to VSCode. Rename the `.env.sample` file to `.env`. Open this file and update `SLACK_APP_TOKEN` and `SLACK_BOT_TOKEN` with the values you previously saved. It will look like this, with your actual token values where you see `` and ``: @@ -111,7 +113,7 @@ SLACK_BOT_TOKEN= Now save the file and try starting your app: ```sh -npm start +python app.py ``` You'll know the local development server is up and running successfully when it emits a bunch of `[DEBUG]` statements to your terminal, the last one containing `connected:ready`. diff --git a/docs/english/tutorial/custom-steps.md b/docs/english/tutorial/custom-steps.md index 66dc16198..50bd723ce 100644 --- a/docs/english/tutorial/custom-steps.md +++ b/docs/english/tutorial/custom-steps.md @@ -111,9 +111,9 @@ Here is a sample app manifest laying out a step definition. This definition tell "name": "user_id" } }, - "required": { + "required": [ "user_id" - } + ] }, "output_parameters": { "properties": { @@ -124,9 +124,9 @@ Here is a sample app manifest laying out a step definition. This definition tell "name": "user_id" } }, - "required": { + "required": [ "user_id" - } + ] }, } } @@ -157,7 +157,7 @@ Notice in the example code here that the name of the step, `sample_step`, is the ```py @app.function("sample_step") -def handle_sample_step_event(inputs: dict, fail: Fail, complete: Complete,logger: logging.Logger): +def handle_sample_step_event(client: WebClient, inputs: dict, fail: Fail, complete: Complete, logger: logging.Logger): user_id = inputs["user_id"] try: client.chat_postMessage( @@ -226,7 +226,7 @@ The second argument is the callback function, or the logic that will run when yo Field | Description ------|------------ `client` | A `WebClient` instance used to make things happen in Slack. From sending messages to opening modals, `client` makes it all happen. For a full list of available methods, refer to the [Web API methods](/reference/methods). Read more about the `WebClient` for Bolt Python [here](https://docs.slack.dev/tools/bolt-python/concepts/web-api/). -`complete` | A utility method that invokes `functions.completeSuccess`. This method indicates to Slack that a step has completed successfully without issue. When called, `complete` requires you include an `outputs` object that matches your step definition in [`output_parameters`](#inputs-outputs). +`complete` | A utility method that invokes `functions.completeSuccess`. This method indicates to Slack that a step has completed successfully without issue. When called, `complete` accepts an optional `outputs` object that matches your step definition in [`output_parameters`](#inputs-outputs). `fail` | A utility method that invokes `functions.completeError`. True to its name, this method signals to Slack that a step has failed to complete. The `fail` method requires an argument of `error` to be sent along with it, which is used to help users understand what went wrong. `inputs` | An alias for the `input_parameters` that were provided to the step upon execution. From a38510f7d5bb2046c8494f23a2f0b6f83f909e7f Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Mon, 1 Jun 2026 15:36:17 +0200 Subject: [PATCH 239/282] fix(falcon/async_resource): replace the use of removed resp.body (#1516) Co-authored-by: William Bergamin --- slack_bolt/adapter/falcon/async_resource.py | 7 ++++--- slack_bolt/adapter/falcon/resource.py | 7 ++++--- tests/adapter_tests/falcon/test_falcon.py | 14 ++++++++++++++ tests/adapter_tests_async/test_async_falcon.py | 15 +++++++++++++++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/slack_bolt/adapter/falcon/async_resource.py b/slack_bolt/adapter/falcon/async_resource.py index 8d03b456c..fdb2d975f 100644 --- a/slack_bolt/adapter/falcon/async_resource.py +++ b/slack_bolt/adapter/falcon/async_resource.py @@ -1,6 +1,7 @@ from datetime import datetime from http import HTTPStatus +from falcon import MEDIA_TEXT from falcon import version as falcon_version from falcon.asgi import Request, Response from slack_bolt import BoltResponse @@ -40,9 +41,9 @@ async def on_get(self, req: Request, resp: Response): await self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..." async def on_post(self, req: Request, resp: Response): bolt_req = await self._to_bolt_request(req) diff --git a/slack_bolt/adapter/falcon/resource.py b/slack_bolt/adapter/falcon/resource.py index 53792775f..5d162ad23 100644 --- a/slack_bolt/adapter/falcon/resource.py +++ b/slack_bolt/adapter/falcon/resource.py @@ -1,6 +1,7 @@ from datetime import datetime from http import HTTPStatus +from falcon import MEDIA_TEXT from falcon import Request, Response, version as falcon_version from slack_bolt import BoltResponse @@ -34,9 +35,9 @@ def on_get(self, req: Request, resp: Response): self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..." def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) diff --git a/tests/adapter_tests/falcon/test_falcon.py b/tests/adapter_tests/falcon/test_falcon.py index d7841a24a..4a2f34d36 100644 --- a/tests/adapter_tests/falcon/test_falcon.py +++ b/tests/adapter_tests/falcon/test_falcon.py @@ -205,3 +205,17 @@ def test_oauth(self): response = client.simulate_get("/slack/install") assert response.status_code == 200 assert "https://slack.com/oauth/v2/authorize?state=" in response.text + + def test_get_no_oauth(self): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + api = new_falcon_app() + resource = SlackAppResource(app) + api.add_route("/slack/events", resource) + + client = testing.TestClient(api) + response = client.simulate_get("/slack/events") + assert response.status_code == 404 + assert "The page is not found" in response.text diff --git a/tests/adapter_tests_async/test_async_falcon.py b/tests/adapter_tests_async/test_async_falcon.py index 5602ed6fb..d7a8c277a 100644 --- a/tests/adapter_tests_async/test_async_falcon.py +++ b/tests/adapter_tests_async/test_async_falcon.py @@ -208,3 +208,18 @@ async def test_oauth(self): assert response.headers.get("content-type") == "text/html; charset=utf-8" assert response.headers.get("content-length") == "607" assert "https://slack.com/oauth/v2/authorize?state=" in response.text + + @pytest.mark.asyncio + async def test_get_no_oauth(self): + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + ) + api = new_falcon_app() + resource = AsyncSlackAppResource(app) + api.add_route("/slack/events", resource) + + async with ASGIConductor(api) as conductor: + response = await conductor.simulate_get("/slack/events") + assert response.status_code == 404 + assert "The page is not found" in response.text From e48100f2b7c2237a419a6226aa9b56cb68446cdc Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 2 Jun 2026 12:30:29 -0400 Subject: [PATCH 240/282] fix: align WSGI/ASGI test infrastructure and handler with spec (#1513) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/adapter_dev.txt | 2 - requirements/test_adapter.txt | 1 - requirements/test_async.txt | 2 + slack_bolt/adapter/asgi/base_handler.py | 20 ++-- slack_bolt/adapter/asgi/http_response.py | 11 +- slack_bolt/adapter/wsgi/handler.py | 21 ++-- slack_bolt/adapter/wsgi/http_request.py | 9 +- slack_bolt/adapter/wsgi/http_response.py | 11 +- tests/adapter_tests/asgi/test_asgi_http.py | 53 +++++++++ .../adapter_tests/asgi/test_asgi_lifespan.py | 19 ++++ tests/mock_asgi_server.py | 90 ++++++++------- tests/mock_wsgi_server.py | 103 +++++++++++------- 12 files changed, 230 insertions(+), 112 deletions(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index ea924f5bb..ee2ad9b83 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -28,6 +28,4 @@ starlette>=0.19.1,<0.45; python_version<"3.9" starlette>=0.49.3,<1; python_version>="3.9" tornado>=6.2,<7; python_version<"3.9" tornado>=6.5.6,<7; python_version>="3.9" -uvicorn<1 # The oldest version can vary among Python runtime versions -gunicorn>=23.0.0,<24 websocket_client>=1.2.3,<2 # Socket Mode 3rd party implementation diff --git a/requirements/test_adapter.txt b/requirements/test_adapter.txt index cb81cd057..ecb3741f2 100644 --- a/requirements/test_adapter.txt +++ b/requirements/test_adapter.txt @@ -1,5 +1,4 @@ # pip install -r requirements/test_adapter.txt moto>=3,<6 # For AWS tests -docker>=5,<8 # Used by moto boddle>=0.2.9,<0.3 # For Bottle app tests sanic-testing>=0.7 diff --git a/requirements/test_async.txt b/requirements/test_async.txt index a2533430e..e74af3982 100644 --- a/requirements/test_async.txt +++ b/requirements/test_async.txt @@ -1,4 +1,6 @@ # pip install -r requirements/test_async.txt -r test.txt -r async_dev.txt +asgiref>=3.7.2,<3.8; python_version<"3.9" +asgiref>=3.8,<4; python_version>="3.9" pytest-asyncio<2; diff --git a/slack_bolt/adapter/asgi/base_handler.py b/slack_bolt/adapter/asgi/base_handler.py index 5e68c51f4..adfa060c1 100644 --- a/slack_bolt/adapter/asgi/base_handler.py +++ b/slack_bolt/adapter/asgi/base_handler.py @@ -1,4 +1,4 @@ -from typing import Callable, Dict, Union +from typing import Callable, Union from .http_request import AsgiHttpRequest from .http_response import AsgiHttpResponse @@ -47,15 +47,13 @@ async def _get_http_response(self, method: str, path: str, request: AsgiHttpRequ return AsgiHttpResponse(status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body) return AsgiHttpResponse(status=404, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Not Found") - async def _handle_lifespan(self, receive: Callable) -> Dict[str, str]: - while True: - lifespan = await receive() - if lifespan["type"] == "lifespan.startup": - """Do something before startup""" - return {"type": "lifespan.startup.complete"} - if lifespan["type"] == "lifespan.shutdown": - """Do something before shutdown""" - return {"type": "lifespan.shutdown.complete"} + async def _handle_lifespan(self, receive: Callable, send: Callable) -> None: + message = await receive() + if message["type"] == "lifespan.startup": + await send({"type": "lifespan.startup.complete"}) + message = await receive() + if message["type"] == "lifespan.shutdown": + await send({"type": "lifespan.shutdown.complete"}) async def __call__(self, scope: scope_type, receive: Callable, send: Callable) -> None: if scope["type"] == "http": @@ -66,6 +64,6 @@ async def __call__(self, scope: scope_type, receive: Callable, send: Callable) - await send(response.get_response_body()) return if scope["type"] == "lifespan": - await send(await self._handle_lifespan(receive)) + await self._handle_lifespan(receive, send) return raise TypeError(f"Unsupported scope type: {scope['type']!r}") diff --git a/slack_bolt/adapter/asgi/http_response.py b/slack_bolt/adapter/asgi/http_response.py index c8178b8f5..58969f137 100644 --- a/slack_bolt/adapter/asgi/http_response.py +++ b/slack_bolt/adapter/asgi/http_response.py @@ -8,11 +8,14 @@ class AsgiHttpResponse: def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): self.status: int = status - self.raw_headers: List[Tuple[bytes, bytes]] = [ - (bytes(key, ENCODING), bytes(value[0], ENCODING)) for key, value in headers.items() - ] - self.raw_headers.append((b"content-length", bytes(str(len(body)), ENCODING))) self.body: bytes = bytes(body, ENCODING) + self.raw_headers: List[Tuple[bytes, bytes]] = [] + for key, values in headers.items(): + if key.lower() == "content-length": + continue + for v in values: + self.raw_headers.append((bytes(key, ENCODING), bytes(v, ENCODING))) + self.raw_headers.append((b"content-length", bytes(str(len(self.body)), ENCODING))) def get_response_start(self) -> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]]: return { diff --git a/slack_bolt/adapter/wsgi/handler.py b/slack_bolt/adapter/wsgi/handler.py index 2861b4425..fef54f73e 100644 --- a/slack_bolt/adapter/wsgi/handler.py +++ b/slack_bolt/adapter/wsgi/handler.py @@ -1,6 +1,10 @@ -from typing import Any, Callable, Dict, Iterable, List, Tuple +from typing import TYPE_CHECKING, Iterable from slack_bolt import App + +if TYPE_CHECKING: + from wsgiref.types import StartResponse, WSGIEnvironment + from slack_bolt.adapter.wsgi.http_request import WsgiHttpRequest from slack_bolt.adapter.wsgi.http_response import WsgiHttpResponse from slack_bolt.request import BoltRequest @@ -69,14 +73,17 @@ def _get_http_response(self, request: WsgiHttpRequest) -> WsgiHttpResponse: def __call__( self, - environ: Dict[str, Any], - start_response: Callable[[str, List[Tuple[str, str]]], None], + environ: "WSGIEnvironment", + start_response: "StartResponse", ) -> Iterable[bytes]: request = WsgiHttpRequest(environ) - if "HTTP" in request.protocol: + if request.protocol.startswith("HTTP"): response: WsgiHttpResponse = self._get_http_response( request=request, ) - start_response(response.status, response.get_headers()) - return response.get_body() - raise TypeError(f"Unsupported SERVER_PROTOCOL: {request.protocol}") + else: + response = WsgiHttpResponse( + status=400, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Bad Request" + ) + start_response(response.status, response.get_headers()) + return response.get_body() diff --git a/slack_bolt/adapter/wsgi/http_request.py b/slack_bolt/adapter/wsgi/http_request.py index 460d8f531..644d0e333 100644 --- a/slack_bolt/adapter/wsgi/http_request.py +++ b/slack_bolt/adapter/wsgi/http_request.py @@ -1,4 +1,7 @@ -from typing import Any, Dict, Sequence, Union +from typing import TYPE_CHECKING, Dict, Sequence, Union + +if TYPE_CHECKING: + from wsgiref.types import WSGIEnvironment from .internals import ENCODING @@ -12,7 +15,7 @@ class WsgiHttpRequest: __slots__ = ("method", "path", "query_string", "protocol", "environ") - def __init__(self, environ: Dict[str, Any]): + def __init__(self, environ: "WSGIEnvironment"): self.method: str = environ.get("REQUEST_METHOD", "GET") self.path: str = environ.get("PATH_INFO", "") self.query_string: str = environ.get("QUERY_STRING", "") @@ -33,5 +36,5 @@ def get_headers(self) -> Dict[str, Union[str, Sequence[str]]]: def get_body(self) -> str: if "wsgi.input" not in self.environ: return "" - content_length = int(self.environ.get("CONTENT_LENGTH", 0)) + content_length = int(self.environ.get("CONTENT_LENGTH") or 0) return self.environ["wsgi.input"].read(content_length).decode(ENCODING) diff --git a/slack_bolt/adapter/wsgi/http_response.py b/slack_bolt/adapter/wsgi/http_response.py index 1ad32e672..32956d276 100644 --- a/slack_bolt/adapter/wsgi/http_response.py +++ b/slack_bolt/adapter/wsgi/http_response.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Dict, Iterable, List, Sequence, Tuple +from typing import Dict, Iterable, List, Optional, Sequence, Tuple from .internals import ENCODING @@ -13,18 +13,19 @@ class WsgiHttpResponse: __slots__ = ("status", "_headers", "_body") - def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): + def __init__(self, status: int, headers: Optional[Dict[str, Sequence[str]]] = None, body: str = ""): _status = HTTPStatus(status) self.status = f"{_status.value} {_status.phrase}" - self._headers = headers + self._headers = headers or {} self._body = bytes(body, ENCODING) def get_headers(self) -> List[Tuple[str, str]]: headers: List[Tuple[str, str]] = [] - for key, value in self._headers.items(): + for key, values in self._headers.items(): if key.lower() == "content-length": continue - headers.append((key, value[0])) + for v in values: + headers.append((key, v)) headers.append(("content-length", str(len(self._body)))) return headers diff --git a/tests/adapter_tests/asgi/test_asgi_http.py b/tests/adapter_tests/asgi/test_asgi_http.py index 72b6434bf..f9f106461 100644 --- a/tests/adapter_tests/asgi/test_asgi_http.py +++ b/tests/adapter_tests/asgi/test_asgi_http.py @@ -223,6 +223,59 @@ async def test_url_verification(self): assert response.headers.get("content-type") == "application/json;charset=utf-8" assert_auth_test_count(self, 1) + @pytest.mark.asyncio + async def test_content_length_multibyte_body(self): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + + def command_handler(ack): + ack(text="Hello ☃") # snowman is 3 bytes in UTF-8 + + app.command("/hello-world")(command_handler) + + body = ( + "token=verification_token" + "&team_id=T111" + "&team_domain=test-domain" + "&channel_id=C111" + "&channel_name=random" + "&user_id=W111" + "&user_name=primary-owner" + "&command=%2Fhello-world" + "&text=Hi" + "&enterprise_id=E111" + "&enterprise_name=Org+Name" + "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx" + "&trigger_id=111.111.xxx" + ) + + headers = self.build_raw_headers(str(int(time())), body) + + asgi_server = AsgiTestServer(SlackRequestHandler(app)) + response = await asgi_server.http("POST", headers, body) + + assert response.status_code == 200 + content_length = int(response.headers.get("content-length")) + actual_bytes = len(response.body.encode("utf-8")) + assert content_length == actual_bytes + + @pytest.mark.asyncio + async def test_multi_value_headers(self): + from slack_bolt.adapter.asgi.http_response import AsgiHttpResponse + + headers = { + "set-cookie": ["cookie1=value1; Path=/", "cookie2=value2; Path=/"], + "content-type": ["text/html; charset=utf-8"], + } + response = AsgiHttpResponse(status=200, headers=headers, body="OK") + + set_cookie_headers = [(name, value) for name, value in response.raw_headers if name == b"set-cookie"] + assert len(set_cookie_headers) == 2 + assert set_cookie_headers[0] == (b"set-cookie", b"cookie1=value1; Path=/") + assert set_cookie_headers[1] == (b"set-cookie", b"cookie2=value2; Path=/") + @pytest.mark.asyncio async def test_unsupported_method(self): app = App( diff --git a/tests/adapter_tests/asgi/test_asgi_lifespan.py b/tests/adapter_tests/asgi/test_asgi_lifespan.py index 488a990f1..5b48f6b8a 100644 --- a/tests/adapter_tests/asgi/test_asgi_lifespan.py +++ b/tests/adapter_tests/asgi/test_asgi_lifespan.py @@ -1,5 +1,6 @@ import pytest +from asgiref.testing import ApplicationCommunicator from slack_sdk.signature import SignatureVerifier from slack_sdk.web import WebClient @@ -59,6 +60,24 @@ async def test_shutdown(self): assert response.type == "lifespan.shutdown.complete" assert response.message == "" + @pytest.mark.asyncio + async def test_full_lifespan_cycle(self): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + + scope = {"type": "lifespan", "asgi": {"version": "3.0", "spec_version": "2.3"}} + communicator = ApplicationCommunicator(SlackRequestHandler(app), scope) + + await communicator.send_input({"type": "lifespan.startup"}) + startup_response = await communicator.receive_output(timeout=1) + assert startup_response["type"] == "lifespan.startup.complete" + + await communicator.send_input({"type": "lifespan.shutdown"}) + shutdown_response = await communicator.receive_output(timeout=1) + assert shutdown_response["type"] == "lifespan.shutdown.complete" + @pytest.mark.asyncio async def test_failed_event(self): app = App( diff --git a/tests/mock_asgi_server.py b/tests/mock_asgi_server.py index e71a0d3cb..0b29a3e5a 100644 --- a/tests/mock_asgi_server.py +++ b/tests/mock_asgi_server.py @@ -1,28 +1,44 @@ -from typing import Iterable, Tuple, Union +from typing import Iterable, Tuple + +from asgiref.testing import ApplicationCommunicator + from slack_bolt.adapter.asgi.base_handler import BaseSlackRequestHandler ENCODING = "utf-8" class AsgiTestServerResponse: - def __init__(self): - self.status_code: int = None - self._headers: Iterable[Tuple[bytes, bytes]] = [] - self._body: bytearray = bytearray(b"") + def __init__( + self, + status_code: int, + headers: Iterable[Tuple[bytes, bytes]] = (), + body: bytes = b"", + ): + self.status_code = status_code + self._headers = headers + self._body = body @property - def body(self): + def body(self) -> str: return self._body.decode(ENCODING) @property - def headers(self): - return {header[0].decode(ENCODING): header[1].decode(ENCODING) for header in self._headers} + def headers(self) -> dict: + result = {} + for header in self._headers: + key = header[0].decode(ENCODING) + if key not in result: + result[key] = header[1].decode(ENCODING) + return result + + def get_headers_list(self, name: str) -> list: + return [header[1].decode(ENCODING) for header in self._headers if header[0].decode(ENCODING) == name] class AsgiTestServerLifespanResponse: - def __init__(self): - self.type: str = None - self.message: str = "" + def __init__(self, type: str, message: str = ""): + self.type = type + self.message = message class AsgiTestServer: @@ -61,22 +77,17 @@ async def http( }, ) - async def receive(): - return {"type": "http.request", "body": bytes(body, ENCODING), "more_body": False} + communicator = ApplicationCommunicator(self.asgi_app, scope) + await communicator.send_input({"type": "http.request", "body": bytes(body, ENCODING), "more_body": False}) - response = AsgiTestServerResponse() + response_start = await communicator.receive_output(timeout=1) + response_body = await communicator.receive_output(timeout=1) - async def send(event): - if event["type"] == "http.response.start": - response.status_code = event["status"] - response._headers = event["headers"] - elif event["type"] == "http.response.body": - response._body.extend(event["body"]) - else: - raise TypeError(f"Sent type {event['type']} in response {event} is not valid") - - await self.asgi_app(scope, receive, send) - return response + return AsgiTestServerResponse( + status_code=response_start["status"], + headers=response_start.get("headers", []), + body=response_body.get("body", b""), + ) async def lifespan(self, event: str) -> AsgiTestServerLifespanResponse: """This implements the server side behavior of the lifespan event @@ -92,17 +103,20 @@ async def lifespan(self, event: str) -> AsgiTestServerLifespanResponse: }, ) - async def receive(): - return {"type": f"lifespan.{event}"} + communicator = ApplicationCommunicator(self.asgi_app, scope) + await communicator.send_input({"type": f"lifespan.{event}"}) - response = AsgiTestServerLifespanResponse() + result = await communicator.receive_output(timeout=1) - async def send(event: dict): - response.type = event["type"] - response.message = event.get("message", "") + # Send shutdown so the handler exits cleanly + if event == "startup": + await communicator.send_input({"type": "lifespan.shutdown"}) + await communicator.receive_output(timeout=1) - await self.asgi_app(scope, receive, send) - return response + return AsgiTestServerLifespanResponse( + type=result["type"], + message=result.get("message", ""), + ) async def websocket(self) -> None: """This is not implemented""" @@ -113,10 +127,6 @@ async def websocket(self) -> None: }, ) - async def receive(): - return {} - - async def send(event: dict): - print(event) - - await self.asgi_app(scope, receive, send) + communicator = ApplicationCommunicator(self.asgi_app, scope) + await communicator.send_input({}) + await communicator.receive_output(timeout=1) diff --git a/tests/mock_wsgi_server.py b/tests/mock_wsgi_server.py index a389a898e..7e8bad2e8 100644 --- a/tests/mock_wsgi_server.py +++ b/tests/mock_wsgi_server.py @@ -1,4 +1,7 @@ -from typing import Dict, Iterable, Optional, Tuple +import io +from typing import Any, Callable, Dict, List, Optional, Tuple +from wsgiref.util import setup_testing_defaults +from wsgiref.validate import validator from slack_bolt.adapter.wsgi import SlackRequestHandler @@ -6,37 +9,49 @@ class WsgiTestServerResponse: - def __init__(self): + def __init__(self) -> None: self.status: Optional[str] = None - self._headers: Iterable[Tuple[str, str]] = [] - self._body: Iterable[bytes] = [] + self._headers: List[Tuple[str, str]] = [] + self._body: List[bytes] = [] @property def headers(self) -> Dict[str, str]: return {header[0]: header[1] for header in self._headers} @property - def body(self, length: int = 0) -> str: - return "".join([chunk.decode(ENCODING) for chunk in self._body[length:]]) + def body(self) -> str: + return "".join([chunk.decode(ENCODING) for chunk in self._body]) class MockReadable: + """PEP 3333 compliant input stream. + + Implements read, readline, readlines, and __iter__ as required + by the WSGI specification for wsgi.input. + """ + def __init__(self, body: str): self.body = body - self._body = bytes(body, ENCODING) + self._stream = io.BytesIO(bytes(body, ENCODING)) def get_content_length(self) -> int: - return len(self._body) + return len(self.body.encode(ENCODING)) + + def read(self, size: int = -1) -> bytes: + if size == -1: + return self._stream.read() + return self._stream.read(size) + + def readline(self, size: int = -1) -> bytes: + if size == -1: + return self._stream.readline() + return self._stream.readline(size) - def read(self, size: int) -> bytes: - if size < 0: - raise ValueError("Size must be positive.") - if size == 0: - return b"" - # The body can only be read once - _body = self._body[:size] - self._body = b"" - return _body + def readlines(self, hint: int = -1) -> List[bytes]: + return self._stream.readlines(hint) + + def __iter__(self): + return iter(self._stream) class WsgiTestServer: @@ -44,29 +59,26 @@ def __init__( self, wsgi_app: SlackRequestHandler, root_path: str = "", - version: Tuple[int, int] = (1, 0), - multithread: bool = False, - multiprocess: bool = False, - run_once: bool = False, input_terminated: bool = True, - server_software: bool = "mock/0.0.0", + server_software: str = "mock/0.0.0", url_scheme: str = "https", remote_addr: str = "127.0.0.1", remote_port: str = "63263", ): self.root_path = root_path - self.wsgi_app = wsgi_app - self.environ = { - "wsgi.version": version, - "wsgi.multithread": multithread, - "wsgi.multiprocess": multiprocess, - "wsgi.run_once": run_once, - "wsgi.input_terminated": input_terminated, - "SERVER_SOFTWARE": server_software, - "wsgi.url_scheme": url_scheme, - "REMOTE_ADDR": remote_addr, - "REMOTE_PORT": remote_port, - } + self.wsgi_app = validator(wsgi_app) + self.environ: Dict[str, Any] = {} + setup_testing_defaults(self.environ) + self.environ.update( + { + "wsgi.input_terminated": input_terminated, + "wsgi.errors": io.StringIO(), + "SERVER_SOFTWARE": server_software, + "wsgi.url_scheme": url_scheme, + "REMOTE_ADDR": remote_addr, + "REMOTE_PORT": remote_port, + } + ) def http( self, @@ -101,16 +113,29 @@ def http( environ[f"HTTP_{header_key}"] = value if body is not None: - environ["wsgi.input"] = MockReadable(body) + readable = MockReadable(body) + environ["wsgi.input"] = readable if "CONTENT_LENGTH" not in environ: - environ["CONTENT_LENGTH"] = str(environ["wsgi.input"].get_content_length()) + environ["CONTENT_LENGTH"] = str(readable.get_content_length()) + else: + environ["wsgi.input"] = MockReadable("") response = WsgiTestServerResponse() - def start_response(status, headers): + def start_response( + status: str, + headers: List[Tuple[str, str]], + exc_info: Optional[Any] = None, + ) -> Callable[[bytes], object]: response.status = status response._headers = headers - - response._body = self.wsgi_app(environ=environ, start_response=start_response) + return lambda s: None + + iterator = self.wsgi_app(environ, start_response) + try: + response._body = list(iterator) + finally: + if hasattr(iterator, "close"): + iterator.close() return response From 315b748cf064f523f436c1e31bed14c8528bb05e Mon Sep 17 00:00:00 2001 From: Salesforce OSPO Service Bot Date: Tue, 2 Jun 2026 15:04:43 -0400 Subject: [PATCH 241/282] Upload required SECURITY.md file for compliance --- SECURITY.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..b69c021ed --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +## Security + +Please report any security issue to [https://www.sfdc.co/SubmitVuln](https://www.sfdc.co/SubmitVuln) +as soon as it is discovered. This library limits its runtime dependencies in +order to reduce the total cost of ownership as much as can be, but all consumers +should remain vigilant and have their security stakeholders review all third-party +products (3PP) like this one and their dependencies. From c1a7d8def0a4cd14890d173d5b4f246f19896a1e Mon Sep 17 00:00:00 2001 From: Salesforce OSPO Service Bot Date: Tue, 2 Jun 2026 15:26:33 -0400 Subject: [PATCH 242/282] Upload required SECURITY.md file for compliance From 704b99ca1e1b6a86dcfda5c70ab9cd190251f47f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:55:01 +0000 Subject: [PATCH 243/282] chore(deps): bump codecov/codecov-action from 6.0.0 to 6.0.1 (#1521) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index ab8cce3e2..78bb83f87 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -126,7 +126,7 @@ jobs: pytest tests/scenario_tests_async/ --junitxml=reports/test_scenario_async.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: directory: ./reports/ fail_ci_if_error: true @@ -162,7 +162,7 @@ jobs: run: | pytest --cov=./slack_bolt/ --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: fail_ci_if_error: true report_type: coverage From 1f2a12928edf4385265526a30c14ed42db68743b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:59:46 +0000 Subject: [PATCH 244/282] chore(deps): bump actions/checkout from 6.0.2 to 6.0.3 (#1523) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 78bb83f87..013f0e216 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -20,7 +20,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -37,7 +37,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -77,7 +77,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} @@ -144,7 +144,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 964eb2c77..56493454d 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ github.event.release.tag_name || github.ref }} persist-credentials: false From f7370002a7e4be9df600b9d8f5779c42d56d0399 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:05:22 +0000 Subject: [PATCH 245/282] chore(deps): bump actions/stale from 10.2.0 to 10.3.0 (#1522) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/triage-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index c29bface2..9d99c40da 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -16,7 +16,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 with: days-before-issue-stale: 30 days-before-issue-close: 10 From c5b719db24df464da7c9b4cb18b77db48119c692 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:44:24 +0000 Subject: [PATCH 246/282] chore(deps): update asgiref requirement from <4,>=3.8 to >=3.11.1,<4 (#1528) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/test_async.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/test_async.txt b/requirements/test_async.txt index e74af3982..8fa6c6806 100644 --- a/requirements/test_async.txt +++ b/requirements/test_async.txt @@ -2,5 +2,5 @@ -r test.txt -r async_dev.txt asgiref>=3.7.2,<3.8; python_version<"3.9" -asgiref>=3.8,<4; python_version>="3.9" +asgiref>=3.11.1,<4; python_version>="3.9" pytest-asyncio<2; From 595aa975a71f2c12e8a136424e5e72c8a3956ae0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:19:49 +0000 Subject: [PATCH 247/282] chore(deps-dev): update aiohttp requirement from <4,>=3 to >=3.13.5,<4 (#1527) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/async_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/async_dev.txt b/requirements/async_dev.txt index c9641cc6f..606d10fef 100644 --- a/requirements/async_dev.txt +++ b/requirements/async_dev.txt @@ -1,3 +1,4 @@ # pip install -r requirements/async_dev.txt -aiohttp>=3,<4 +aiohttp>=3,<4; python_version<"3.9" +aiohttp>=3.13.5,<4; python_version>="3.9" websockets<16 From 6dd69f8f68753bf1da3d672f0874bce6232116b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:48:42 +0000 Subject: [PATCH 248/282] chore(deps-dev): update websocket-client requirement from <2,>=1.2.3 to >=1.9.0,<2 (#1526) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index ee2ad9b83..00259471c 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -28,4 +28,5 @@ starlette>=0.19.1,<0.45; python_version<"3.9" starlette>=0.49.3,<1; python_version>="3.9" tornado>=6.2,<7; python_version<"3.9" tornado>=6.5.6,<7; python_version>="3.9" -websocket_client>=1.2.3,<2 # Socket Mode 3rd party implementation +websocket_client>=1.2.3,<1.9; python_version<"3.9" # Socket Mode 3rd party implementation +websocket_client>=1.9.0,<2; python_version>="3.9" # Socket Mode 3rd party implementation From c1a66dc18abbc8f25427d7b327580dc5fb3cdc17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:56:24 +0000 Subject: [PATCH 249/282] chore(deps-dev): update flask requirement from <4,>=1 to >=3.1.3,<4 (#1525) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin --- requirements/adapter_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 00259471c..c179de2f5 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -12,7 +12,8 @@ Django>=4.2.30,<6; python_version>="3.8" falcon>=2,<4; python_version<"3.9" falcon>=4.2.0,<5; python_version>="3.9" fastapi>=0.70.0,<1 -Flask>=1,<4 +Flask>=1,<4; python_version<"3.9" +Flask>=3.1.3,<4; python_version>="3.9" Werkzeug>=2,<3; python_version<"3.9" Werkzeug>=3.1.8,<4; python_version>="3.9" pyramid>=1,<3 From a370e1160739295bab722bc472fef92be95bf806 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 17:30:08 +0000 Subject: [PATCH 250/282] chore(deps-dev): update fastapi requirement from <1,>=0.70.0 to >=0.128.8,<1 (#1524) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index c179de2f5..14dfa9c84 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -11,7 +11,8 @@ Django>=3.2,<4; python_version<"3.8" Django>=4.2.30,<6; python_version>="3.8" falcon>=2,<4; python_version<"3.9" falcon>=4.2.0,<5; python_version>="3.9" -fastapi>=0.70.0,<1 +fastapi>=0.70.0,<1; python_version<"3.9" +fastapi>=0.128.8,<1; python_version>="3.9" Flask>=1,<4; python_version<"3.9" Flask>=3.1.3,<4; python_version>="3.9" Werkzeug>=2,<3; python_version<"3.9" From 96fd215841bcc020e03e3ff512334397f95ef356 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:57:10 +0000 Subject: [PATCH 251/282] chore(deps): bump codecov/codecov-action from 6.0.1 to 7.0.0 (#1530) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 013f0e216..7bfefd017 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -126,7 +126,7 @@ jobs: pytest tests/scenario_tests_async/ --junitxml=reports/test_scenario_async.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: directory: ./reports/ fail_ci_if_error: true @@ -162,7 +162,7 @@ jobs: run: | pytest --cov=./slack_bolt/ --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: fail_ci_if_error: true report_type: coverage From 76338bf390fc07f689258194b9d5051858a514a0 Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Fri, 12 Jun 2026 12:21:05 -0500 Subject: [PATCH 252/282] docs: removing external links for unified nav (#1531) --- docs/english/_sidebar.json | 16 ---------------- docs/english/index.md | 4 ++++ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/docs/english/_sidebar.json b/docs/english/_sidebar.json index 79721bdcd..aa75b0c15 100644 --- a/docs/english/_sidebar.json +++ b/docs/english/_sidebar.json @@ -202,21 +202,5 @@ "items": ["tools/bolt-python/ja-jp/legacy/steps-from-apps"] } ] - }, - { "type": "html", "value": "
      " }, - { - "type": "link", - "label": "Release notes", - "href": "https://github.com/slackapi/bolt-python/releases" - }, - { - "type": "link", - "label": "Code on GitHub", - "href": "https://github.com/SlackAPI/bolt-python" - }, - { - "type": "link", - "label": "Contributors Guide", - "href": "https://github.com/SlackAPI/bolt-python/blob/main/.github/contributing.md" } ] diff --git a/docs/english/index.md b/docs/english/index.md index 212bd9690..60df4ec1c 100644 --- a/docs/english/index.md +++ b/docs/english/index.md @@ -13,6 +13,10 @@ If you otherwise get stuck, we're here to help. The following are the best ways * [Issue Tracker](http://github.com/slackapi/bolt-python/issues) for questions, bug reports, feature requests, and general discussion related to Bolt for Python. Try searching for an existing issue before creating a new one. * [Email](mailto:support@slack.com) our developer support team: `support@slack.com`. +## Release notes + +Check out the [Bolt for Python release notes](https://github.com/slackapi/bolt-python/releases) for all the latest happenings. + ## Contributing These docs live within the [Bolt-Python](https://github.com/slackapi/bolt-python/) repository and are open source. From 03d4add4fa8877c95b647cbd2f759233c900eefb Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:54:10 -0500 Subject: [PATCH 253/282] docs: fixing broken links (#1532) --- docs/english/concepts/adding-agent-features.md | 4 ++-- docs/english/creating-an-app.md | 4 ++-- docs/english/getting-started.md | 4 ++-- docs/japanese/getting-started.md | 4 ++-- docs/reference/app/app.html | 4 ++-- docs/reference/app/index.html | 4 ++-- docs/reference/index.html | 6 +++--- examples/aws_lambda/README.md | 8 ++++---- examples/django/README.md | 6 +++--- examples/getting_started/README.md | 4 ++-- slack_bolt/__init__.py | 2 +- slack_bolt/app/app.py | 2 +- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/english/concepts/adding-agent-features.md b/docs/english/concepts/adding-agent-features.md index 87a8d31cb..865a8af4e 100644 --- a/docs/english/concepts/adding-agent-features.md +++ b/docs/english/concepts/adding-agent-features.md @@ -10,7 +10,7 @@ The code snippets throughout this guide are from our [Support Agent sample app]( View our [agent quickstart](/ai/agent-quickstart) to get up and running with Casey. Otherwise, read on for exploration and explanation of agent-focused Bolt features found within Casey. ::: -Your agent can utilize features applicable to messages throughout Slack, like [chat streaming](#text-streaming) and [feedback buttons](#adding-and-handling-feedback). They can also [utilize the `Assistant` class](/tools/bolt-python/concepts/assistant-class) for a side-panel view designed with AI in mind. +Your agent can utilize features applicable to messages throughout Slack, like [chat streaming](#text-streaming) and [feedback buttons](#adding-and-handling-feedback). They can also [utilize the `Assistant` class](/tools/bolt-python/concepts/using-the-assistant-class) for a side-panel view designed with AI in mind. If you're unfamiliar with using these feature within Slack, you may want to read the [API docs on the subject](/ai/). Then come back here to implement them with Bolt! @@ -204,7 +204,7 @@ def handle_message( :::tip[Using the Assistant side panel] -The Assistant side panel requires additional setup. See the [Assistant class guide](/tools/bolt-python/concepts/assistant-class). +The Assistant side panel requires additional setup. See the [Assistant class guide](/tools/bolt-python/concepts/using-the-assistant-class). ::: diff --git a/docs/english/creating-an-app.md b/docs/english/creating-an-app.md index 7f06e9d42..66e1febee 100644 --- a/docs/english/creating-an-app.md +++ b/docs/english/creating-an-app.md @@ -55,7 +55,7 @@ We're going to use bot and app-level tokens for this guide. :::tip[Not sharing is sometimes caring] -Treat your tokens like passwords and [keep them safe](/security). Your app uses tokens to post and retrieve information from Slack workspaces. +Treat your tokens like passwords and [keep them safe](/concepts/security). Your app uses tokens to post and retrieve information from Slack workspaces. ::: @@ -103,7 +103,7 @@ $ export SLACK_APP_TOKEN= :::warning[Keep it secret. Keep it safe.] -Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](/security). +Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](/concepts/security). ::: diff --git a/docs/english/getting-started.md b/docs/english/getting-started.md index 6964df23b..8cfd7faf8 100644 --- a/docs/english/getting-started.md +++ b/docs/english/getting-started.md @@ -12,7 +12,7 @@ When complete, you'll have a local environment configured with a customized [app :::tip[Reference for readers] -In search of the complete guide to building an app from scratch? Check out the [building an app](/tools/bolt-python/building-an-app) guide. +In search of the complete guide to building an app from scratch? Check out the [building an app](/tools/bolt-python/creating-an-app) guide. ::: @@ -147,7 +147,7 @@ The above command works on Linux and macOS but [similar commands are available o :::warning[Keep it secret. Keep it safe.] -Treat your tokens like a password and [keep it safe](/security). Your app uses these to retrieve and send information to Slack. +Treat your tokens like a password and [keep it safe](/concepts/security). Your app uses these to retrieve and send information to Slack. ::: diff --git a/docs/japanese/getting-started.md b/docs/japanese/getting-started.md index 41e6ae5cd..46ac55ffb 100644 --- a/docs/japanese/getting-started.md +++ b/docs/japanese/getting-started.md @@ -48,7 +48,7 @@ Slack アプリで使用できるトークンには、ユーザートークン 6. 左サイドメニューの「**Socket Mode**」を有効にします。 -:::tip[トークンはパスワードと同様に取り扱い、[安全な方法で保管してください](/security)。アプリはこのトークンを使って Slack ワークスペースで投稿をしたり、情報の取得をしたりします。] +:::tip[トークンはパスワードと同様に取り扱い、[安全な方法で保管してください](/concepts/security)。アプリはこのトークンを使って Slack ワークスペースで投稿をしたり、情報の取得をしたりします。] ::: @@ -91,7 +91,7 @@ export SLACK_APP_TOKEN=<アプリレベルトークン> ``` :::warning[🔒 全てのトークンは安全に保管してください。] -少なくともパブリックなバージョン管理にチェックインするようなことは避けるべきでしょう。また、上にあった例のように環境変数を介してアクセスするようにしてください。詳細な情報は [アプリのセキュリティのベストプラクティス](/security)のドキュメントを参照してください。 +少なくともパブリックなバージョン管理にチェックインするようなことは避けるべきでしょう。また、上にあった例のように環境変数を介してアクセスするようにしてください。詳細な情報は [アプリのセキュリティのベストプラクティス](/concepts/security)のドキュメントを参照してください。 ::: diff --git a/docs/reference/app/app.html b/docs/reference/app/app.html index bf0d5ee00..737597548 100644 --- a/docs/reference/app/app.html +++ b/docs/reference/app/app.html @@ -118,7 +118,7 @@

      Classes

      if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. + Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. @@ -1438,7 +1438,7 @@

      Classes

      if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) -

      Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details.

      +

      Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details.

      If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

      Args

      diff --git a/docs/reference/app/index.html b/docs/reference/app/index.html index 32e006944..5581b98e7 100644 --- a/docs/reference/app/index.html +++ b/docs/reference/app/index.html @@ -137,7 +137,7 @@

      Classes

      if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. + Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. @@ -1457,7 +1457,7 @@

      Classes

      if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) -

      Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details.

      +

      Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details.

      If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

      Args

      diff --git a/docs/reference/index.html b/docs/reference/index.html index b2d19719d..2903c9b7f 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -36,7 +36,7 @@

      Package slack_bolt

      -

      A Python framework to build Slack apps in a flash with the latest platform features.Read the getting started guide and look at our code examples to learn how to build apps using Bolt.

      +

      A Python framework to build Slack apps in a flash with the latest platform features.Read the getting started guide and look at our code examples to learn how to build apps using Bolt.

      • Website: https://docs.slack.dev/tools/bolt-python/
      • GitHub repository: https://github.com/slackapi/bolt-python
      • @@ -258,7 +258,7 @@

        Class variables

        if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. + Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. @@ -1578,7 +1578,7 @@

        Class variables

        if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) -

        Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details.

        +

        Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details.

        If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app.

        Args

        diff --git a/examples/aws_lambda/README.md b/examples/aws_lambda/README.md index 49a8f7da2..2fc34b7cf 100644 --- a/examples/aws_lambda/README.md +++ b/examples/aws_lambda/README.md @@ -33,15 +33,15 @@ Instructions on how to set up and deploy each example are provided below. - Optionally enter a description for the role, such as "Bolt Python basic role" 3. Ensure you have created an app on api.slack.com/apps as per the - [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. + [Building an App](https://docs.slack.dev/tools/bolt-python/creating-an-app) guide. Ensure you have installed it to a workspace. 4. Ensure you have exported your Slack Bot Token and Slack Signing Secret for your apps as the environment variables `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET`, respectively, as per the - [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. + [Building an App](https://docs.slack.dev/tools/bolt-python/creating-an-app) guide. 5. You may want to create a dedicated virtual environment for this example app, as per the "Setting up your project" section of the - [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. + [Building an App](https://docs.slack.dev/tools/bolt-python/creating-an-app) guide. 6. Let's deploy the Lambda! Run `./deploy_lazy.sh`. By default it deploys to the us-east-1 region in AWS - you can change this at the top of `lazy_aws_lambda_config.yaml` if you wish. 7. Load up AWS Lambda inside the AWS Console - make sure you are in the correct @@ -150,7 +150,7 @@ Let’s create a user role that will use the custom policy we created as well as 3. "Create Role" ### Create Slack App and Load your Lambda to AWS -Ensure you have created an app on [api.slack.com/apps](https://api.slack.com/apps) as per the [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide. You do not need to ensure you have installed it to a workspace, as the OAuth flow will provide your app the ability to be installed by anyone. +Ensure you have created an app on [api.slack.com/apps](https://api.slack.com/apps) as per the [Building an App](https://docs.slack.dev/tools/bolt-python/creating-an-app) guide. You do not need to ensure you have installed it to a workspace, as the OAuth flow will provide your app the ability to be installed by anyone. 1. Remember those S3 buckets we made? You will need the names of these buckets again in the next step. 2. You need many environment variables exported! Specifically the following from api.slack.com/apps diff --git a/examples/django/README.md b/examples/django/README.md index ca0460fd1..cb29a822b 100644 --- a/examples/django/README.md +++ b/examples/django/README.md @@ -4,7 +4,7 @@ This example demonstrates how you can use Bolt for Python in your Django applica ### `simple_app` - Single-workspace App Example -If you want to run a simple app like the one you've tried in the [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide, this is the right one for you. By default, this Django project runs this application. If you want to switch to OAuth flow supported one, modify `myslackapp/urls.py`. +If you want to run a simple app like the one you've tried in the [Building an App](https://docs.slack.dev/tools/bolt-python/creating-an-app) guide, this is the right one for you. By default, this Django project runs this application. If you want to switch to OAuth flow supported one, modify `myslackapp/urls.py`. To run this app, all you need to do are: @@ -31,7 +31,7 @@ python manage.py migrate python manage.py runserver 0.0.0.0:3000 ``` -As you did at [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide, configure ngrok or something similar to serve a public endpoint. Lastly, +As you did at [Building an App](https://docs.slack.dev/tools/bolt-python/creating-an-app) guide, configure ngrok or something similar to serve a public endpoint. Lastly, * Go back to the Slack app configuration page * Go to "Event Subscriptions" @@ -73,7 +73,7 @@ python manage.py migrate python manage.py runserver 0.0.0.0:3000 ``` -As you did at [Building an App](https://docs.slack.dev/tools/bolt-python/building-an-app) guide, configure ngrok or something similar to serve a public endpoint. Lastly, +As you did at [Building an App](https://docs.slack.dev/tools/bolt-python/creating-an-app) guide, configure ngrok or something similar to serve a public endpoint. Lastly, * Go back to the Slack app configuration page * Go to "Event Subscriptions" diff --git a/examples/getting_started/README.md b/examples/getting_started/README.md index 5d3c2f61d..48f9d4095 100644 --- a/examples/getting_started/README.md +++ b/examples/getting_started/README.md @@ -42,6 +42,6 @@ ngrok http 3000 python3 app.py ``` -[1]: https://docs.slack.dev/tools/bolt-python/building-an-app +[1]: https://docs.slack.dev/tools/bolt-python/creating-an-app [2]: https://docs.slack.dev/tools/bolt-python/ -[3]: https://docs.slack.dev/tools/bolt-python/building-an-app#setting-up-events +[3]: https://docs.slack.dev/tools/bolt-python/creating-an-app#setting-up-events diff --git a/slack_bolt/__init__.py b/slack_bolt/__init__.py index d85453950..e3664814b 100644 --- a/slack_bolt/__init__.py +++ b/slack_bolt/__init__.py @@ -1,5 +1,5 @@ """ -A Python framework to build Slack apps in a flash with the latest platform features.Read the [getting started guide](https://docs.slack.dev/tools/bolt-python/building-an-app) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. +A Python framework to build Slack apps in a flash with the latest platform features.Read the [getting started guide](https://docs.slack.dev/tools/bolt-python/creating-an-app) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. * Website: https://docs.slack.dev/tools/bolt-python/ * GitHub repository: https://github.com/slackapi/bolt-python diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index 0af27913c..e20649902 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -156,7 +156,7 @@ def message_hello(message, say): if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) - Refer to https://docs.slack.dev/tools/bolt-python/building-an-app for details. + Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. From 29b9dbdd250bd9f1d27bdee10251656f6491fbbd Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 22 Jun 2026 09:57:08 -0400 Subject: [PATCH 254/282] docs: restore canonical security policy over bot-added SECURITY.md (#1533) Co-authored-by: Claude --- .github/SECURITY.md | 63 ----------------------------------------- SECURITY.md | 68 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 69 deletions(-) delete mode 100644 .github/SECURITY.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index 06dfdb664..000000000 --- a/.github/SECURITY.md +++ /dev/null @@ -1,63 +0,0 @@ -# Security Policy - -Slack takes the security of its software and services seriously, including all open-source repositories managed through the [slackapi](https://github.com/slackapi) GitHub organization. - -## Reporting a Vulnerability - -**Do NOT report security vulnerabilities through public GitHub issues, pull requests, or discussions.** - -If you believe you have found a security vulnerability in `slack-bolt`, please report it through the Slack bug bounty program on HackerOne: - -**** - -Even if `slack-bolt` is not explicitly listed as an in-scope asset on the HackerOne program page, reports for vulnerabilities in this package should still be submitted there. The Slack security team triages reports for all `slackapi` open-source repositories through this program. - -If HackerOne is inaccessible, you may alternatively report the issue to [security@salesforce.com](mailto:security@salesforce.com). - -Please do not discuss potential vulnerabilities in public without first coordinating with the security team. - -## What to Include - -To help us triage and respond quickly, please include: - -- Type of vulnerability (e.g., signature bypass, token leakage, denial of service) -- Affected version(s) of `slack-bolt` -- Step-by-step reproduction instructions -- Proof-of-concept code or payloads, if available -- Impact assessment: what an attacker could achieve -- Any specific configuration required to trigger the vulnerability -- Affected source file paths, if known - -## Threat Model - -Bolt for Python is a framework that sits between the Slack platform and developer application code. Its security boundary covers the integrity and confidentiality of that interface. - -### In Scope - -The following are considered framework vulnerabilities: - -- Bypass of request signature verification (HMAC-SHA256 validation) -- OAuth token leakage or cross-tenant token exposure during authorization flows -- Denial of service caused by malformed or specially crafted payloads processed by framework internals -- Authentication or authorization bypass in any built-in adapter -- Information disclosure through framework error responses or timing side channels -- Bypass of the `ssl_check` endpoint protections - -### Out of Scope - -The following are NOT framework vulnerabilities: - -- Vulnerabilities in the Python runtime, operating system, or hosting infrastructure -- Security issues in developer application logic built on top of Bolt (e.g., SQL injection caused by passing unsanitized payload data to a database) -- Vulnerabilities in third-party PyPI packages chosen and installed by the developer outside of Bolt's direct dependencies -- Vulnerabilities in Slack's server-side platform infrastructure (report those directly under Slack's main HackerOne scope) -- Attacks that require possession of a valid signing secret or bot token -- Arbitrary attribute injection or unsafe deserialization caused by developer code handling untrusted input -- Issues that only affect end-of-life versions with no reproduction on supported versions - -## Disclosure Policy - -This project follows coordinated disclosure: - -- Allow a reasonable timeframe for the team to investigate, develop, and release a fix before any public disclosure. -- Researchers who follow responsible disclosure practices are eligible for recognition and bounty consideration through the Slack HackerOne program. diff --git a/SECURITY.md b/SECURITY.md index b69c021ed..06dfdb664 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,7 +1,63 @@ -## Security +# Security Policy -Please report any security issue to [https://www.sfdc.co/SubmitVuln](https://www.sfdc.co/SubmitVuln) -as soon as it is discovered. This library limits its runtime dependencies in -order to reduce the total cost of ownership as much as can be, but all consumers -should remain vigilant and have their security stakeholders review all third-party -products (3PP) like this one and their dependencies. +Slack takes the security of its software and services seriously, including all open-source repositories managed through the [slackapi](https://github.com/slackapi) GitHub organization. + +## Reporting a Vulnerability + +**Do NOT report security vulnerabilities through public GitHub issues, pull requests, or discussions.** + +If you believe you have found a security vulnerability in `slack-bolt`, please report it through the Slack bug bounty program on HackerOne: + +**** + +Even if `slack-bolt` is not explicitly listed as an in-scope asset on the HackerOne program page, reports for vulnerabilities in this package should still be submitted there. The Slack security team triages reports for all `slackapi` open-source repositories through this program. + +If HackerOne is inaccessible, you may alternatively report the issue to [security@salesforce.com](mailto:security@salesforce.com). + +Please do not discuss potential vulnerabilities in public without first coordinating with the security team. + +## What to Include + +To help us triage and respond quickly, please include: + +- Type of vulnerability (e.g., signature bypass, token leakage, denial of service) +- Affected version(s) of `slack-bolt` +- Step-by-step reproduction instructions +- Proof-of-concept code or payloads, if available +- Impact assessment: what an attacker could achieve +- Any specific configuration required to trigger the vulnerability +- Affected source file paths, if known + +## Threat Model + +Bolt for Python is a framework that sits between the Slack platform and developer application code. Its security boundary covers the integrity and confidentiality of that interface. + +### In Scope + +The following are considered framework vulnerabilities: + +- Bypass of request signature verification (HMAC-SHA256 validation) +- OAuth token leakage or cross-tenant token exposure during authorization flows +- Denial of service caused by malformed or specially crafted payloads processed by framework internals +- Authentication or authorization bypass in any built-in adapter +- Information disclosure through framework error responses or timing side channels +- Bypass of the `ssl_check` endpoint protections + +### Out of Scope + +The following are NOT framework vulnerabilities: + +- Vulnerabilities in the Python runtime, operating system, or hosting infrastructure +- Security issues in developer application logic built on top of Bolt (e.g., SQL injection caused by passing unsanitized payload data to a database) +- Vulnerabilities in third-party PyPI packages chosen and installed by the developer outside of Bolt's direct dependencies +- Vulnerabilities in Slack's server-side platform infrastructure (report those directly under Slack's main HackerOne scope) +- Attacks that require possession of a valid signing secret or bot token +- Arbitrary attribute injection or unsafe deserialization caused by developer code handling untrusted input +- Issues that only affect end-of-life versions with no reproduction on supported versions + +## Disclosure Policy + +This project follows coordinated disclosure: + +- Allow a reasonable timeframe for the team to investigate, develop, and release a fix before any public disclosure. +- Researchers who follow responsible disclosure practices are eligible for recognition and bounty consideration through the Slack HackerOne program. From 7fdd404ef376baa2fce8e51e256fbbb99cf7f6a9 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 30 Jun 2026 13:07:44 -0700 Subject: [PATCH 255/282] fix: defer SignatureVerifier construction so Socket Mode apps init without a signing secret (#1541) Co-authored-by: William Bergamin --- .../request_verification.py | 12 +++++++++-- tests/scenario_tests/test_app.py | 7 +++++++ .../test_request_verification.py | 19 ++++++++++++++++++ .../test_request_verification.py | 20 +++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/slack_bolt/middleware/request_verification/request_verification.py b/slack_bolt/middleware/request_verification/request_verification.py index c0f3f5c31..af505bc84 100644 --- a/slack_bolt/middleware/request_verification/request_verification.py +++ b/slack_bolt/middleware/request_verification/request_verification.py @@ -1,5 +1,5 @@ from logging import Logger -from typing import Callable, Dict, Any, Optional +from typing import Any, Callable, Dict, Optional from slack_sdk.signature import SignatureVerifier @@ -20,9 +20,17 @@ def __init__(self, signing_secret: str, base_logger: Optional[Logger] = None): signing_secret: The signing secret base_logger: The base logger """ - self.verifier = SignatureVerifier(signing_secret=signing_secret) + self._signing_secret = signing_secret + self._verifier: Optional[SignatureVerifier] = None self.logger = get_bolt_logger(RequestVerification, base_logger=base_logger) + @property + def verifier(self) -> SignatureVerifier: + # Defer initialization to avoid errors during start up + if self._verifier is None: + self._verifier = SignatureVerifier(signing_secret=self._signing_secret) + return self._verifier + def process( self, *, diff --git a/tests/scenario_tests/test_app.py b/tests/scenario_tests/test_app.py index 9fe6f423f..5cdff39bf 100644 --- a/tests/scenario_tests/test_app.py +++ b/tests/scenario_tests/test_app.py @@ -96,6 +96,13 @@ def test_token_verification_enabled_False(self): assert self.received_requests.get("/auth.test") is None + def test_socket_mode_app_without_signing_secret(self): + app = App( + client=self.web_client, + token_verification_enabled=False, + ) + assert app is not None + # -------------------------- # multi teams auth # -------------------------- diff --git a/tests/slack_bolt/middleware/request_verification/test_request_verification.py b/tests/slack_bolt/middleware/request_verification/test_request_verification.py index ae163a84d..53af43bf3 100644 --- a/tests/slack_bolt/middleware/request_verification/test_request_verification.py +++ b/tests/slack_bolt/middleware/request_verification/test_request_verification.py @@ -1,5 +1,6 @@ from time import time +import pytest from slack_sdk.signature import SignatureVerifier from slack_bolt.middleware import RequestVerification @@ -60,3 +61,21 @@ def test_ssl_check_param_requires_valid_signature(self): resp = middleware.process(req=req, resp=resp, next=next) assert resp.status == 401 assert resp.body == """{"error": "invalid request"}""" + + def test_empty_signing_secret_does_not_raise_on_init(self): + RequestVerification(signing_secret="") + + def test_socket_mode_request_skips_verification_without_signing_secret(self): + middleware = RequestVerification(signing_secret="") + req = BoltRequest(mode="socket_mode", body="payload={}", headers={}) + resp = BoltResponse(status=404, body="default") + resp = middleware.process(req=req, resp=resp, next=next) + assert resp.status == 200 + assert resp.body == "next" + + def test_http_request_with_empty_signing_secret_raises(self): + middleware = RequestVerification(signing_secret="") + req = BoltRequest(body="payload={}", headers={}) + resp = BoltResponse(status=404) + with pytest.raises(ValueError): + middleware.process(req=req, resp=resp, next=next) diff --git a/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py b/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py index 28921bc87..126b04f94 100644 --- a/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py +++ b/tests/slack_bolt_async/middleware/request_verification/test_request_verification.py @@ -66,3 +66,23 @@ async def test_ssl_check_param_requires_valid_signature(self): resp = await middleware.async_process(req=req, resp=resp, next=next) assert resp.status == 401 assert resp.body == """{"error": "invalid request"}""" + + def test_empty_signing_secret_does_not_raise_on_init(self): + AsyncRequestVerification(signing_secret="") + + @pytest.mark.asyncio + async def test_socket_mode_request_skips_verification_without_signing_secret(self): + middleware = AsyncRequestVerification(signing_secret="") + req = AsyncBoltRequest(mode="socket_mode", body="payload={}", headers={}) + resp = BoltResponse(status=404, body="default") + resp = await middleware.async_process(req=req, resp=resp, next=next) + assert resp.status == 200 + assert resp.body == "next" + + @pytest.mark.asyncio + async def test_http_request_with_empty_signing_secret_raises(self): + middleware = AsyncRequestVerification(signing_secret="") + req = AsyncBoltRequest(body="payload={}", headers={}) + resp = BoltResponse(status=404) + with pytest.raises(ValueError): + await middleware.async_process(req=req, resp=resp, next=next) From 8028f0003be21f909da1a387345a907d1e0f5e96 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 30 Jun 2026 13:20:51 -0700 Subject: [PATCH 256/282] chore(release): version 1.29.0 (#1542) --- docs/reference/adapter/asgi/base_handler.html | 18 ++++---- .../reference/adapter/asgi/http_response.html | 44 ++++++++++++------- .../adapter/falcon/async_resource.html | 12 ++--- docs/reference/adapter/falcon/index.html | 12 ++--- docs/reference/adapter/falcon/resource.html | 12 ++--- .../adapter/socket_mode/async_internals.html | 2 +- .../adapter/socket_mode/internals.html | 22 +++++++++- docs/reference/adapter/wsgi/handler.html | 15 ++++--- docs/reference/adapter/wsgi/http_request.html | 28 ++++++------ .../reference/adapter/wsgi/http_response.html | 25 ++++++----- docs/reference/adapter/wsgi/index.html | 15 ++++--- docs/reference/async_app.html | 15 +++++++ .../context/say_stream/async_say_stream.html | 9 ++++ docs/reference/context/say_stream/index.html | 9 ++++ .../context/say_stream/say_stream.html | 9 ++++ .../context/set_status/async_set_status.html | 6 +++ docs/reference/context/set_status/index.html | 6 +++ .../context/set_status/set_status.html | 6 +++ docs/reference/index.html | 15 +++++++ docs/reference/middleware/index.html | 33 +++++++++++++- .../request_verification/index.html | 33 +++++++++++++- .../request_verification.html | 33 +++++++++++++- docs/reference/request/internals.html | 4 +- slack_bolt/version.py | 2 +- 24 files changed, 293 insertions(+), 92 deletions(-) diff --git a/docs/reference/adapter/asgi/base_handler.html b/docs/reference/adapter/asgi/base_handler.html index b8a6da68f..74358683e 100644 --- a/docs/reference/adapter/asgi/base_handler.html +++ b/docs/reference/adapter/asgi/base_handler.html @@ -88,15 +88,13 @@

        Classes

        return AsgiHttpResponse(status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body) return AsgiHttpResponse(status=404, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Not Found") - async def _handle_lifespan(self, receive: Callable) -> Dict[str, str]: - while True: - lifespan = await receive() - if lifespan["type"] == "lifespan.startup": - """Do something before startup""" - return {"type": "lifespan.startup.complete"} - if lifespan["type"] == "lifespan.shutdown": - """Do something before shutdown""" - return {"type": "lifespan.shutdown.complete"} + async def _handle_lifespan(self, receive: Callable, send: Callable) -> None: + message = await receive() + if message["type"] == "lifespan.startup": + await send({"type": "lifespan.startup.complete"}) + message = await receive() + if message["type"] == "lifespan.shutdown": + await send({"type": "lifespan.shutdown.complete"}) async def __call__(self, scope: scope_type, receive: Callable, send: Callable) -> None: if scope["type"] == "http": @@ -107,7 +105,7 @@

        Classes

        await send(response.get_response_body()) return if scope["type"] == "lifespan": - await send(await self._handle_lifespan(receive)) + await self._handle_lifespan(receive, send) return raise TypeError(f"Unsupported scope type: {scope['type']!r}") diff --git a/docs/reference/adapter/asgi/http_response.html b/docs/reference/adapter/asgi/http_response.html index 86e368f6e..0c42d9a9f 100644 --- a/docs/reference/adapter/asgi/http_response.html +++ b/docs/reference/adapter/asgi/http_response.html @@ -60,11 +60,14 @@

        Classes

        def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): self.status: int = status - self.raw_headers: List[Tuple[bytes, bytes]] = [ - (bytes(key, ENCODING), bytes(value[0], ENCODING)) for key, value in headers.items() - ] - self.raw_headers.append((b"content-length", bytes(str(len(body)), ENCODING))) self.body: bytes = bytes(body, ENCODING) + self.raw_headers: List[Tuple[bytes, bytes]] = [] + for key, values in headers.items(): + if key.lower() == "content-length": + continue + for v in values: + self.raw_headers.append((bytes(key, ENCODING), bytes(v, ENCODING))) + self.raw_headers.append((b"content-length", bytes(str(len(self.body)), ENCODING))) def get_response_start(self) -> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]]: return { @@ -94,11 +97,14 @@

        Instance variables

        def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): self.status: int = status - self.raw_headers: List[Tuple[bytes, bytes]] = [ - (bytes(key, ENCODING), bytes(value[0], ENCODING)) for key, value in headers.items() - ] - self.raw_headers.append((b"content-length", bytes(str(len(body)), ENCODING))) self.body: bytes = bytes(body, ENCODING) + self.raw_headers: List[Tuple[bytes, bytes]] = [] + for key, values in headers.items(): + if key.lower() == "content-length": + continue + for v in values: + self.raw_headers.append((bytes(key, ENCODING), bytes(v, ENCODING))) + self.raw_headers.append((b"content-length", bytes(str(len(self.body)), ENCODING))) def get_response_start(self) -> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]]: return { @@ -127,11 +133,14 @@

        Instance variables

        def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): self.status: int = status - self.raw_headers: List[Tuple[bytes, bytes]] = [ - (bytes(key, ENCODING), bytes(value[0], ENCODING)) for key, value in headers.items() - ] - self.raw_headers.append((b"content-length", bytes(str(len(body)), ENCODING))) self.body: bytes = bytes(body, ENCODING) + self.raw_headers: List[Tuple[bytes, bytes]] = [] + for key, values in headers.items(): + if key.lower() == "content-length": + continue + for v in values: + self.raw_headers.append((bytes(key, ENCODING), bytes(v, ENCODING))) + self.raw_headers.append((b"content-length", bytes(str(len(self.body)), ENCODING))) def get_response_start(self) -> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]]: return { @@ -160,11 +169,14 @@

        Instance variables

        def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): self.status: int = status - self.raw_headers: List[Tuple[bytes, bytes]] = [ - (bytes(key, ENCODING), bytes(value[0], ENCODING)) for key, value in headers.items() - ] - self.raw_headers.append((b"content-length", bytes(str(len(body)), ENCODING))) self.body: bytes = bytes(body, ENCODING) + self.raw_headers: List[Tuple[bytes, bytes]] = [] + for key, values in headers.items(): + if key.lower() == "content-length": + continue + for v in values: + self.raw_headers.append((bytes(key, ENCODING), bytes(v, ENCODING))) + self.raw_headers.append((b"content-length", bytes(str(len(self.body)), ENCODING))) def get_response_start(self) -> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]]: return { diff --git a/docs/reference/adapter/falcon/async_resource.html b/docs/reference/adapter/falcon/async_resource.html index f43ab11ef..0dbba1ad4 100644 --- a/docs/reference/adapter/falcon/async_resource.html +++ b/docs/reference/adapter/falcon/async_resource.html @@ -85,9 +85,9 @@

        Classes

        await self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..." async def on_post(self, req: Request, resp: Response): bolt_req = await self._to_bolt_request(req) @@ -149,9 +149,9 @@

        Methods

        await self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..."
      diff --git a/docs/reference/adapter/falcon/index.html b/docs/reference/adapter/falcon/index.html index 82a2a57e2..bfc21828f 100644 --- a/docs/reference/adapter/falcon/index.html +++ b/docs/reference/adapter/falcon/index.html @@ -91,9 +91,9 @@

      Classes

      self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..." def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -159,9 +159,9 @@

      Methods

      self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..."
      diff --git a/docs/reference/adapter/falcon/resource.html b/docs/reference/adapter/falcon/resource.html index 73860adc1..13f9a2177 100644 --- a/docs/reference/adapter/falcon/resource.html +++ b/docs/reference/adapter/falcon/resource.html @@ -80,9 +80,9 @@

      Classes

      self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..." def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) @@ -148,9 +148,9 @@

      Methods

      self._write_response(bolt_resp, resp) return - resp.status = "404" - # Falcon 4.x w/ mypy fails to correctly infer the str type here - resp.body = "The page is not found..." + resp.status = HTTPStatus.NOT_FOUND + resp.content_type = MEDIA_TEXT + resp.text = "The page is not found..."
      diff --git a/docs/reference/adapter/socket_mode/async_internals.html b/docs/reference/adapter/socket_mode/async_internals.html index d2e300efa..c0b23b1de 100644 --- a/docs/reference/adapter/socket_mode/async_internals.html +++ b/docs/reference/adapter/socket_mode/async_internals.html @@ -54,7 +54,7 @@

      Functions

      Expand source code
      async def run_async_bolt_app(app: AsyncApp, req: SocketModeRequest):
      -    bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload)
      +    bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload, headers=build_headers(req))
           bolt_resp: BoltResponse = await app.async_dispatch(bolt_req)
           return bolt_resp
      diff --git a/docs/reference/adapter/socket_mode/internals.html b/docs/reference/adapter/socket_mode/internals.html index 55d96b054..ba7d2f226 100644 --- a/docs/reference/adapter/socket_mode/internals.html +++ b/docs/reference/adapter/socket_mode/internals.html @@ -45,6 +45,25 @@

      Module slack_bolt.adapter.socket_mode.internals

      Functions

      +
      +def build_headers(req: slack_sdk.socket_mode.request.SocketModeRequest) ‑> Dict[str, str | Sequence[str]] | None +
      +
      +
      + +Expand source code + +
      def build_headers(req: SocketModeRequest) -> Optional[Dict[str, Union[str, Sequence[str]]]]:
      +    # Mirror the HTTP mode retry headers so middleware/listeners can detect Events API retries
      +    headers: Dict[str, Union[str, Sequence[str]]] = {}
      +    if req.retry_attempt is not None:
      +        headers["x-slack-retry-num"] = str(req.retry_attempt)
      +    if req.retry_reason is not None:
      +        headers["x-slack-retry-reason"] = req.retry_reason
      +    return headers or None
      +
      +
      +
      def run_bolt_app(app: App,
      req: slack_sdk.socket_mode.request.SocketModeRequest)
      @@ -54,7 +73,7 @@

      Functions

      Expand source code
      def run_bolt_app(app: App, req: SocketModeRequest):
      -    bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload)
      +    bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload, headers=build_headers(req))
           bolt_resp: BoltResponse = app.dispatch(bolt_req)
           return bolt_resp
      @@ -111,6 +130,7 @@

      Functions

    • Functions

      diff --git a/docs/reference/adapter/wsgi/handler.html b/docs/reference/adapter/wsgi/handler.html index 204499a05..a6ea85ca4 100644 --- a/docs/reference/adapter/wsgi/handler.html +++ b/docs/reference/adapter/wsgi/handler.html @@ -117,17 +117,20 @@

      Classes

      def __call__( self, - environ: Dict[str, Any], - start_response: Callable[[str, List[Tuple[str, str]]], None], + environ: "WSGIEnvironment", + start_response: "StartResponse", ) -> Iterable[bytes]: request = WsgiHttpRequest(environ) - if "HTTP" in request.protocol: + if request.protocol.startswith("HTTP"): response: WsgiHttpResponse = self._get_http_response( request=request, ) - start_response(response.status, response.get_headers()) - return response.get_body() - raise TypeError(f"Unsupported SERVER_PROTOCOL: {request.protocol}") + else: + response = WsgiHttpResponse( + status=400, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Bad Request" + ) + start_response(response.status, response.get_headers()) + return response.get_body()

      Setup Bolt as a WSGI web framework, this will make your application compatible with WSGI web servers. This can be used for production deployments.

      diff --git a/docs/reference/adapter/wsgi/http_request.html b/docs/reference/adapter/wsgi/http_request.html index fa845dd93..72c5f28be 100644 --- a/docs/reference/adapter/wsgi/http_request.html +++ b/docs/reference/adapter/wsgi/http_request.html @@ -48,7 +48,7 @@

      Classes

      class WsgiHttpRequest -(environ: Dict[str, Any]) +(environ: WSGIEnvironment)
      @@ -64,7 +64,7 @@

      Classes

      __slots__ = ("method", "path", "query_string", "protocol", "environ") - def __init__(self, environ: Dict[str, Any]): + def __init__(self, environ: "WSGIEnvironment"): self.method: str = environ.get("REQUEST_METHOD", "GET") self.path: str = environ.get("PATH_INFO", "") self.query_string: str = environ.get("QUERY_STRING", "") @@ -85,7 +85,7 @@

      Classes

      def get_body(self) -> str: if "wsgi.input" not in self.environ: return "" - content_length = int(self.environ.get("CONTENT_LENGTH", 0)) + content_length = int(self.environ.get("CONTENT_LENGTH") or 0) return self.environ["wsgi.input"].read(content_length).decode(ENCODING)

      This Class uses the PEP 3333 standard to extract request information @@ -108,7 +108,7 @@

      Instance variables

      __slots__ = ("method", "path", "query_string", "protocol", "environ") - def __init__(self, environ: Dict[str, Any]): + def __init__(self, environ: "WSGIEnvironment"): self.method: str = environ.get("REQUEST_METHOD", "GET") self.path: str = environ.get("PATH_INFO", "") self.query_string: str = environ.get("QUERY_STRING", "") @@ -129,7 +129,7 @@

      Instance variables

      def get_body(self) -> str: if "wsgi.input" not in self.environ: return "" - content_length = int(self.environ.get("CONTENT_LENGTH", 0)) + content_length = int(self.environ.get("CONTENT_LENGTH") or 0) return self.environ["wsgi.input"].read(content_length).decode(ENCODING)
      @@ -149,7 +149,7 @@

      Instance variables

      __slots__ = ("method", "path", "query_string", "protocol", "environ") - def __init__(self, environ: Dict[str, Any]): + def __init__(self, environ: "WSGIEnvironment"): self.method: str = environ.get("REQUEST_METHOD", "GET") self.path: str = environ.get("PATH_INFO", "") self.query_string: str = environ.get("QUERY_STRING", "") @@ -170,7 +170,7 @@

      Instance variables

      def get_body(self) -> str: if "wsgi.input" not in self.environ: return "" - content_length = int(self.environ.get("CONTENT_LENGTH", 0)) + content_length = int(self.environ.get("CONTENT_LENGTH") or 0) return self.environ["wsgi.input"].read(content_length).decode(ENCODING)
      @@ -190,7 +190,7 @@

      Instance variables

      __slots__ = ("method", "path", "query_string", "protocol", "environ") - def __init__(self, environ: Dict[str, Any]): + def __init__(self, environ: "WSGIEnvironment"): self.method: str = environ.get("REQUEST_METHOD", "GET") self.path: str = environ.get("PATH_INFO", "") self.query_string: str = environ.get("QUERY_STRING", "") @@ -211,7 +211,7 @@

      Instance variables

      def get_body(self) -> str: if "wsgi.input" not in self.environ: return "" - content_length = int(self.environ.get("CONTENT_LENGTH", 0)) + content_length = int(self.environ.get("CONTENT_LENGTH") or 0) return self.environ["wsgi.input"].read(content_length).decode(ENCODING)
      @@ -231,7 +231,7 @@

      Instance variables

      __slots__ = ("method", "path", "query_string", "protocol", "environ") - def __init__(self, environ: Dict[str, Any]): + def __init__(self, environ: "WSGIEnvironment"): self.method: str = environ.get("REQUEST_METHOD", "GET") self.path: str = environ.get("PATH_INFO", "") self.query_string: str = environ.get("QUERY_STRING", "") @@ -252,7 +252,7 @@

      Instance variables

      def get_body(self) -> str: if "wsgi.input" not in self.environ: return "" - content_length = int(self.environ.get("CONTENT_LENGTH", 0)) + content_length = int(self.environ.get("CONTENT_LENGTH") or 0) return self.environ["wsgi.input"].read(content_length).decode(ENCODING)
      @@ -272,7 +272,7 @@

      Instance variables

      __slots__ = ("method", "path", "query_string", "protocol", "environ") - def __init__(self, environ: Dict[str, Any]): + def __init__(self, environ: "WSGIEnvironment"): self.method: str = environ.get("REQUEST_METHOD", "GET") self.path: str = environ.get("PATH_INFO", "") self.query_string: str = environ.get("QUERY_STRING", "") @@ -293,7 +293,7 @@

      Instance variables

      def get_body(self) -> str: if "wsgi.input" not in self.environ: return "" - content_length = int(self.environ.get("CONTENT_LENGTH", 0)) + content_length = int(self.environ.get("CONTENT_LENGTH") or 0) return self.environ["wsgi.input"].read(content_length).decode(ENCODING)
      @@ -312,7 +312,7 @@

      Methods

      def get_body(self) -> str:
           if "wsgi.input" not in self.environ:
               return ""
      -    content_length = int(self.environ.get("CONTENT_LENGTH", 0))
      +    content_length = int(self.environ.get("CONTENT_LENGTH") or 0)
           return self.environ["wsgi.input"].read(content_length).decode(ENCODING)
      diff --git a/docs/reference/adapter/wsgi/http_response.html b/docs/reference/adapter/wsgi/http_response.html index da7dc33f0..726332c77 100644 --- a/docs/reference/adapter/wsgi/http_response.html +++ b/docs/reference/adapter/wsgi/http_response.html @@ -48,7 +48,7 @@

      Classes

      class WsgiHttpResponse -(status: int, headers: Dict[str, Sequence[str]] = {}, body: str = '') +(status: int, headers: Dict[str, Sequence[str]] | None = None, body: str = '')
      @@ -64,18 +64,19 @@

      Classes

      __slots__ = ("status", "_headers", "_body") - def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): + def __init__(self, status: int, headers: Optional[Dict[str, Sequence[str]]] = None, body: str = ""): _status = HTTPStatus(status) self.status = f"{_status.value} {_status.phrase}" - self._headers = headers + self._headers = headers or {} self._body = bytes(body, ENCODING) def get_headers(self) -> List[Tuple[str, str]]: headers: List[Tuple[str, str]] = [] - for key, value in self._headers.items(): + for key, values in self._headers.items(): if key.lower() == "content-length": continue - headers.append((key, value[0])) + for v in values: + headers.append((key, v)) headers.append(("content-length", str(len(self._body)))) return headers @@ -103,18 +104,19 @@

      Instance variables

      __slots__ = ("status", "_headers", "_body") - def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): + def __init__(self, status: int, headers: Optional[Dict[str, Sequence[str]]] = None, body: str = ""): _status = HTTPStatus(status) self.status = f"{_status.value} {_status.phrase}" - self._headers = headers + self._headers = headers or {} self._body = bytes(body, ENCODING) def get_headers(self) -> List[Tuple[str, str]]: headers: List[Tuple[str, str]] = [] - for key, value in self._headers.items(): + for key, values in self._headers.items(): if key.lower() == "content-length": continue - headers.append((key, value[0])) + for v in values: + headers.append((key, v)) headers.append(("content-length", str(len(self._body)))) return headers @@ -150,10 +152,11 @@

      Methods

      def get_headers(self) -> List[Tuple[str, str]]:
           headers: List[Tuple[str, str]] = []
      -    for key, value in self._headers.items():
      +    for key, values in self._headers.items():
               if key.lower() == "content-length":
                   continue
      -        headers.append((key, value[0]))
      +        for v in values:
      +            headers.append((key, v))
       
           headers.append(("content-length", str(len(self._body))))
           return headers
      diff --git a/docs/reference/adapter/wsgi/index.html b/docs/reference/adapter/wsgi/index.html index c3cfafea1..186d1adf6 100644 --- a/docs/reference/adapter/wsgi/index.html +++ b/docs/reference/adapter/wsgi/index.html @@ -136,17 +136,20 @@

      Classes

      def __call__( self, - environ: Dict[str, Any], - start_response: Callable[[str, List[Tuple[str, str]]], None], + environ: "WSGIEnvironment", + start_response: "StartResponse", ) -> Iterable[bytes]: request = WsgiHttpRequest(environ) - if "HTTP" in request.protocol: + if request.protocol.startswith("HTTP"): response: WsgiHttpResponse = self._get_http_response( request=request, ) - start_response(response.status, response.get_headers()) - return response.get_body() - raise TypeError(f"Unsupported SERVER_PROTOCOL: {request.protocol}") + else: + response = WsgiHttpResponse( + status=400, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Bad Request" + ) + start_response(response.status, response.get_headers()) + return response.get_body()

      Setup Bolt as a WSGI web framework, this will make your application compatible with WSGI web servers. This can be used for production deployments.

      diff --git a/docs/reference/async_app.html b/docs/reference/async_app.html index 3494ec289..6a45d2ada 100644 --- a/docs/reference/async_app.html +++ b/docs/reference/async_app.html @@ -5291,6 +5291,9 @@

      Class variables

      recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncChatStream: """Starts a new chat stream with context.""" @@ -5308,6 +5311,9 @@

      Class variables

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return await self.client.chat_stream( @@ -5315,6 +5321,9 @@

      Class variables

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) @@ -5371,6 +5380,9 @@

      Class variables

      self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncSlackResponse: return await self.client.assistant_threads_setStatus( @@ -5378,6 +5390,9 @@

      Class variables

      thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/context/say_stream/async_say_stream.html b/docs/reference/context/say_stream/async_say_stream.html index 4010b284d..3a1978299 100644 --- a/docs/reference/context/say_stream/async_say_stream.html +++ b/docs/reference/context/say_stream/async_say_stream.html @@ -85,6 +85,9 @@

      Classes

      recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncChatStream: """Starts a new chat stream with context.""" @@ -102,6 +105,9 @@

      Classes

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return await self.client.chat_stream( @@ -109,6 +115,9 @@

      Classes

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/context/say_stream/index.html b/docs/reference/context/say_stream/index.html index 645942c72..5ed62587b 100644 --- a/docs/reference/context/say_stream/index.html +++ b/docs/reference/context/say_stream/index.html @@ -96,6 +96,9 @@

      Classes

      recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> ChatStream: """Starts a new chat stream with context.""" @@ -113,6 +116,9 @@

      Classes

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return self.client.chat_stream( @@ -120,6 +126,9 @@

      Classes

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/context/say_stream/say_stream.html b/docs/reference/context/say_stream/say_stream.html index 784a58bbe..e7bc33bff 100644 --- a/docs/reference/context/say_stream/say_stream.html +++ b/docs/reference/context/say_stream/say_stream.html @@ -85,6 +85,9 @@

      Classes

      recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> ChatStream: """Starts a new chat stream with context.""" @@ -102,6 +105,9 @@

      Classes

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return self.client.chat_stream( @@ -109,6 +115,9 @@

      Classes

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/context/set_status/async_set_status.html b/docs/reference/context/set_status/async_set_status.html index 06efd6447..770583e4a 100644 --- a/docs/reference/context/set_status/async_set_status.html +++ b/docs/reference/context/set_status/async_set_status.html @@ -74,6 +74,9 @@

      Classes

      self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncSlackResponse: return await self.client.assistant_threads_setStatus( @@ -81,6 +84,9 @@

      Classes

      thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/context/set_status/index.html b/docs/reference/context/set_status/index.html index aa11815e3..380e37f4f 100644 --- a/docs/reference/context/set_status/index.html +++ b/docs/reference/context/set_status/index.html @@ -85,6 +85,9 @@

      Classes

      self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> SlackResponse: return self.client.assistant_threads_setStatus( @@ -92,6 +95,9 @@

      Classes

      thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/context/set_status/set_status.html b/docs/reference/context/set_status/set_status.html index e4d839f64..b0a0a9ee7 100644 --- a/docs/reference/context/set_status/set_status.html +++ b/docs/reference/context/set_status/set_status.html @@ -74,6 +74,9 @@

      Classes

      self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> SlackResponse: return self.client.assistant_threads_setStatus( @@ -81,6 +84,9 @@

      Classes

      thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/index.html b/docs/reference/index.html index 2903c9b7f..70d84875a 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -5915,6 +5915,9 @@

      Class variables

      recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> ChatStream: """Starts a new chat stream with context.""" @@ -5932,6 +5935,9 @@

      Class variables

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return self.client.chat_stream( @@ -5939,6 +5945,9 @@

      Class variables

      recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) @@ -5995,6 +6004,9 @@

      Class variables

      self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> SlackResponse: return self.client.assistant_threads_setStatus( @@ -6002,6 +6014,9 @@

      Class variables

      thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/docs/reference/middleware/index.html b/docs/reference/middleware/index.html index ce2629224..9f2053a5d 100644 --- a/docs/reference/middleware/index.html +++ b/docs/reference/middleware/index.html @@ -720,9 +720,17 @@

      Inherited members

      signing_secret: The signing secret base_logger: The base logger """ - self.verifier = SignatureVerifier(signing_secret=signing_secret) + self._signing_secret = signing_secret + self._verifier: Optional[SignatureVerifier] = None self.logger = get_bolt_logger(RequestVerification, base_logger=base_logger) + @property + def verifier(self) -> SignatureVerifier: + # Defer initialization to avoid errors during start up + if self._verifier is None: + self._verifier = SignatureVerifier(signing_secret=self._signing_secret) + return self._verifier + def process( self, *, @@ -749,7 +757,7 @@

      Inherited members

      @staticmethod def _can_skip(mode: str, body: Dict[str, Any]) -> bool: - return mode == "socket_mode" or (body is not None and body.get("ssl_check") == "1") + return mode == "socket_mode" @staticmethod def _build_error_response() -> BoltResponse: @@ -779,6 +787,24 @@

      Subclasses

      +

      Instance variables

      +
      +
      prop verifier : slack_sdk.signature.SignatureVerifier
      +
      +
      + +Expand source code + +
      @property
      +def verifier(self) -> SignatureVerifier:
      +    # Defer initialization to avoid errors during start up
      +    if self._verifier is None:
      +        self._verifier = SignatureVerifier(signing_secret=self._signing_secret)
      +    return self._verifier
      +
      +
      +
      +

      Inherited members

      • Middleware: @@ -1139,6 +1165,9 @@

        RequestVerification

        +
      • SingleTeamAuthorization

        diff --git a/docs/reference/middleware/request_verification/index.html b/docs/reference/middleware/request_verification/index.html index 5dfd6ed82..50a8676b5 100644 --- a/docs/reference/middleware/request_verification/index.html +++ b/docs/reference/middleware/request_verification/index.html @@ -77,9 +77,17 @@

        Classes

        signing_secret: The signing secret base_logger: The base logger """ - self.verifier = SignatureVerifier(signing_secret=signing_secret) + self._signing_secret = signing_secret + self._verifier: Optional[SignatureVerifier] = None self.logger = get_bolt_logger(RequestVerification, base_logger=base_logger) + @property + def verifier(self) -> SignatureVerifier: + # Defer initialization to avoid errors during start up + if self._verifier is None: + self._verifier = SignatureVerifier(signing_secret=self._signing_secret) + return self._verifier + def process( self, *, @@ -106,7 +114,7 @@

        Classes

        @staticmethod def _can_skip(mode: str, body: Dict[str, Any]) -> bool: - return mode == "socket_mode" or (body is not None and body.get("ssl_check") == "1") + return mode == "socket_mode" @staticmethod def _build_error_response() -> BoltResponse: @@ -136,6 +144,24 @@

        Subclasses

        +

        Instance variables

        +
        +
        prop verifier : slack_sdk.signature.SignatureVerifier
        +
        +
        + +Expand source code + +
        @property
        +def verifier(self) -> SignatureVerifier:
        +    # Defer initialization to avoid errors during start up
        +    if self._verifier is None:
        +        self._verifier = SignatureVerifier(signing_secret=self._signing_secret)
        +    return self._verifier
        +
        +
        +
        +

        Inherited members

        • Middleware: @@ -169,6 +195,9 @@

          Inherited members

        • diff --git a/docs/reference/middleware/request_verification/request_verification.html b/docs/reference/middleware/request_verification/request_verification.html index 99134110a..4ee2ed1b1 100644 --- a/docs/reference/middleware/request_verification/request_verification.html +++ b/docs/reference/middleware/request_verification/request_verification.html @@ -66,9 +66,17 @@

          Classes

          signing_secret: The signing secret base_logger: The base logger """ - self.verifier = SignatureVerifier(signing_secret=signing_secret) + self._signing_secret = signing_secret + self._verifier: Optional[SignatureVerifier] = None self.logger = get_bolt_logger(RequestVerification, base_logger=base_logger) + @property + def verifier(self) -> SignatureVerifier: + # Defer initialization to avoid errors during start up + if self._verifier is None: + self._verifier = SignatureVerifier(signing_secret=self._signing_secret) + return self._verifier + def process( self, *, @@ -95,7 +103,7 @@

          Classes

          @staticmethod def _can_skip(mode: str, body: Dict[str, Any]) -> bool: - return mode == "socket_mode" or (body is not None and body.get("ssl_check") == "1") + return mode == "socket_mode" @staticmethod def _build_error_response() -> BoltResponse: @@ -125,6 +133,24 @@

          Subclasses

          +

          Instance variables

          +
          +
          prop verifier : slack_sdk.signature.SignatureVerifier
          +
          +
          + +Expand source code + +
          @property
          +def verifier(self) -> SignatureVerifier:
          +    # Defer initialization to avoid errors during start up
          +    if self._verifier is None:
          +        self._verifier = SignatureVerifier(signing_secret=self._signing_secret)
          +    return self._verifier
          +
          +
          +
          +

          Inherited members

          • Middleware: @@ -152,6 +178,9 @@

            Inherited members

          • diff --git a/docs/reference/request/internals.html b/docs/reference/request/internals.html index bd8319183..d25880135 100644 --- a/docs/reference/request/internals.html +++ b/docs/reference/request/internals.html @@ -432,9 +432,9 @@

            Functions

            if isinstance(payload.get("event"), dict): return extract_team_id(payload["event"]) if isinstance(payload.get("user"), dict): - return payload["user"]["team_id"] + return payload["user"].get("team_id") if isinstance(payload.get("view"), dict): - return payload["view"]["team_id"] + return payload["view"].get("team_id") return None
            diff --git a/slack_bolt/version.py b/slack_bolt/version.py index ebda7dafb..79018b9b2 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.28.0" +__version__ = "1.29.0" From 3978707a1527bdcb5ad82e4009cfddeb76d1dfe0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jun 2026 14:40:05 -0700 Subject: [PATCH 257/282] chore(deps-dev): update cherrypy requirement from <19,>=18 to >=18.10.0,<19 (#1536) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 14dfa9c84..d118106b9 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -6,7 +6,7 @@ bottle>=0.12,<1 chalice>=1.28,<1.31; python_version<"3.9" chalice>=1.32.0,<2; python_version>="3.9" cheroot<12 -CherryPy>=18,<19 +CherryPy>=18.10.0,<19 Django>=3.2,<4; python_version<"3.8" Django>=4.2.30,<6; python_version>="3.8" falcon>=2,<4; python_version<"3.9" From 1f23eee3484bf9577ac050f2c7234e29ba3c6a8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jun 2026 21:43:25 +0000 Subject: [PATCH 258/282] chore(deps-dev): update websockets requirement from <16 to <17 (#1537) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/async_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/async_dev.txt b/requirements/async_dev.txt index 606d10fef..a40433441 100644 --- a/requirements/async_dev.txt +++ b/requirements/async_dev.txt @@ -1,4 +1,4 @@ # pip install -r requirements/async_dev.txt aiohttp>=3,<4; python_version<"3.9" aiohttp>=3.13.5,<4; python_version>="3.9" -websockets<16 +websockets<17 From 23120cc2d403b11475d61bb65981970004533cee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 22:16:03 +0000 Subject: [PATCH 259/282] chore(deps): bump actions/setup-python from 6.2.0 to 6.3.0 (#1544) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 7bfefd017..74c362f15 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -24,7 +24,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Run lint verification @@ -41,7 +41,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Install synchronous dependencies @@ -81,7 +81,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.python-version }} - name: Install synchronous dependencies @@ -148,7 +148,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Install dependencies diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 56493454d..1c171f0ff 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.x" From 12c2da5a0573f89aaeaf67fb31908fb19f8d8243 Mon Sep 17 00:00:00 2001 From: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:42:42 -0500 Subject: [PATCH 260/282] docs: updates for agent_view release (#1543) --- .../english/concepts/adding-agent-features.md | 79 +++++++++++++++---- .../concepts/using-the-assistant-class.md | 10 ++- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/docs/english/concepts/adding-agent-features.md b/docs/english/concepts/adding-agent-features.md index 865a8af4e..f420a219a 100644 --- a/docs/english/concepts/adding-agent-features.md +++ b/docs/english/concepts/adding-agent-features.md @@ -42,7 +42,7 @@ slack install -E local 5. Enable MCP for your app: - Run `slack app settings` to open your app's settings - - Navigate to **Agents & AI Apps** in the left-side navigation + - Navigate to **Agents** in the left-side navigation - Toggle **Model Context Protocol** on 6. Update your `.env` OAuth environment variables: @@ -80,11 +80,10 @@ Agents can be invoked throughout Slack, such as via @mentions in channels, messa import re from logging import Logger -from agents import Runner from slack_bolt import BoltContext, Say, SayStream, SetStatus from slack_sdk import WebClient -from agent import CaseyDeps, casey_agent +from agent import CaseyDeps, run_casey from thread_context import conversation_store from listeners.views.feedback_builder import build_feedback_blocks @@ -201,14 +200,62 @@ def handle_message( - + :::tip[Using the Assistant side panel] -The Assistant side panel requires additional setup. See the [Assistant class guide](/tools/bolt-python/concepts/using-the-assistant-class). +The assistant messaging experience requires additional setup. See the [Assistant class guide](/tools/bolt-python/concepts/using-the-assistant-class). ::: +How you greet a user and set suggested prompts when they open the agent's DM depends on which [messaging experience](/ai/developing-agents) your app uses: -```py +* The **agent messaging experience** (`agent_view`) is the default and the only experience available to apps going forward. Conversations happen in the app's **Messages** tab, so you listen for the [`app_home_opened`](/reference/events/app_home_opened) event and check for the `messages` tab to detect when a user opens the DM, then set suggested prompts at the top of the tab (no `thread_ts` required). Casey uses this approach. +* The **assistant messaging experience** (`assistant_view`) is the legacy experience, with separate **Chat** and **History** tabs. You listen for the [`assistant_thread_started`](/reference/events/assistant_thread_started) event to detect a thread, then set suggested prompts on it. Existing apps can continue to use this but should migrate to the agent messaging experience. + +Refer to the [agent messaging experience changelog entry](/changelog/2026/06/30/agent-messages-tab) for the full list of changes and a migration checklist. + + + + +```python +from logging import Logger + +from slack_bolt import BoltContext +from slack_sdk import WebClient + +SUGGESTED_PROMPTS = [ + {"title": "Reset Password", "message": "I need to reset my password"}, + {"title": "Request Access", "message": "I need access to a system or tool"}, + {"title": "Network Issues", "message": "I'm having network connectivity issues"}, +] + + +def handle_app_home_opened( + client: WebClient, event: dict, context: BoltContext, logger: Logger +): + """Handle app_home_opened events. + + Under agent_view, this event fires for both the Home tab and the Messages + tab (the agent DM). Branch on ``event["tab"]``. + """ + try: + if event.get("tab") == "messages": + # Suggested prompts pin to the top of the Messages tab; no thread_ts required. + client.assistant_threads_setSuggestedPrompts( + channel_id=event["channel"], + title="How can I help you today?", + prompts=SUGGESTED_PROMPTS, + ) + return + + # event["tab"] == "home": publish your App Home Block Kit view here + except Exception as e: + logger.exception(f"Failed to handle app_home_opened: {e}") +``` + + + + +```python from logging import Logger from slack_bolt.context.set_suggested_prompts import SetSuggestedPrompts @@ -236,6 +283,9 @@ def handle_assistant_thread_started( + + + --- ## Setting status {#setting-assistant-status} @@ -395,7 +445,7 @@ from logging import Logger from slack_bolt import BoltContext, Say, SayStream, SetStatus from slack_sdk import WebClient -from agent import CaseyDeps, casey_agent, get_model +from agent import CaseyDeps, run_casey from thread_context import conversation_store from listeners.views.feedback_builder import build_feedback_blocks @@ -456,13 +506,9 @@ def handle_app_mentioned( channel_id=channel_id, thread_ts=thread_ts, message_ts=event["ts"], + user_token=context.user_token, ) - result = casey_agent.run_sync( - cleaned_text, - model=get_model(), - deps=deps, - message_history=history, - ) + result = run_casey(cleaned_text, deps, message_history=history) # Stream response in thread with feedback buttons streamer = say_stream() @@ -554,6 +600,7 @@ def handle_app_mentioned( channel_id=channel_id, thread_ts=thread_ts, message_ts=event["ts"], + user_token=context.user_token, ) response_text, new_session_id = run_casey_agent( cleaned_text, session_id=existing_session_id, deps=deps @@ -583,11 +630,10 @@ def handle_app_mentioned( import re from logging import Logger -from agents import Runner from slack_bolt import BoltContext, Say, SayStream, SetStatus from slack_sdk import WebClient -from agent import CaseyDeps, casey_agent +from agent import CaseyDeps, run_casey from thread_context import conversation_store from listeners.views.feedback_builder import build_feedback_blocks @@ -654,8 +700,9 @@ def handle_app_mentioned( channel_id=channel_id, thread_ts=thread_ts, message_ts=event["ts"], + user_token=context.user_token, ) - result = Runner.run_sync(casey_agent, input=input_items, context=deps) + result = run_casey(input_items, deps) # Stream response in thread with feedback buttons streamer = say_stream() diff --git a/docs/english/concepts/using-the-assistant-class.md b/docs/english/concepts/using-the-assistant-class.md index 40c97d0cd..55ca3d110 100644 --- a/docs/english/concepts/using-the-assistant-class.md +++ b/docs/english/concepts/using-the-assistant-class.md @@ -4,7 +4,11 @@ If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. ::: -The `Assistant` class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. +The `Assistant` class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the **Agents** feature enabled. + +:::warning[The `Assistant` class targets the assistant messaging experience] +The `Assistant` class handles the [assistant messaging experience](/ai/developing-agents) (`assistant_view`), in which agent conversations happen in separate Chat and History tabs. Apps going forward use the agent messaging experience (`agent_view`) by default, where conversations happen in the Messages tab and you handle events such as [`app_home_opened`](/reference/events/app_home_opened) and [`message.im`](/reference/events/message.im) directly. Refer to [Adding agent features](/tools/bolt-python/concepts/adding-agent-features) and the [agent messaging experience changelog entry](/changelog/2026/06/30/agent-messages-tab) for the default experience and migration guidance. +::: A typical flow would look like: @@ -58,7 +62,7 @@ If you do provide your own `threadContextStore` property, it must feature `find` ## Configuring your app to support the `Assistant` class {#configuring-assistant-class} -1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & AI Apps** feature. +1. Within [App Settings](https://api.slack.com/apps), enable the **Agents** feature. 2. Within the App Settings **OAuth & Permissions** page, add the following scopes: * [`assistant:write`](/reference/scopes/assistant.write) @@ -326,4 +330,4 @@ def respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: See the [_Creating agents: adding and handling feedback_](/tools/bolt-python/concepts/adding-agent-features#adding-and-handling-feedback) section for adding feedback buttons with Block Kit. -Want to see the functionality described throughout this guide in action? We've created a [App Agent Template](https://github.com/slack-samples/bolt-python-assistant-template) repo for you to build from. \ No newline at end of file +Want to see the functionality described throughout this guide in action? We've created a [Starter Agent Template](https://github.com/slack-samples/bolt-python-starter-agent) repo for you to build from. \ No newline at end of file From 8fbd5037d31f1efe83f7519108bf2edfa33432d7 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 8 Jul 2026 15:23:04 -0400 Subject: [PATCH 261/282] test: characterization tests for payload_utils assistant predicates (#1548) Co-authored-by: Claude --- tests/scenario_tests/test_events_assistant.py | 4 +- .../scenario_tests/test_events_ignore_self.py | 1 + .../test_events_assistant.py | 4 +- .../test_events_ignore_self.py | 1 + .../slack_bolt/request/test_payload_utils.py | 283 ++++++++++++++++++ 5 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 tests/slack_bolt/request/test_payload_utils.py diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index a1c3f1343..c95296154 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -409,7 +409,7 @@ def build_payload(event: dict) -> dict: "user_profile": {}, "thread_ts": "1726133698.626339", "parent_user_id": "W222", - "channel": "D111", + "channel": "C111", "event_ts": "1726133700.887259", "channel_type": "channel", } @@ -444,7 +444,7 @@ def build_payload(event: dict) -> dict: "reply_users": ["U222", "W111"], "is_locked": False, }, - "channel": "D111", + "channel": "C111", "hidden": True, "ts": "1726133701.028300", "event_ts": "1726133701.028300", diff --git a/tests/scenario_tests/test_events_ignore_self.py b/tests/scenario_tests/test_events_ignore_self.py index db7d07ea8..3c66f3278 100644 --- a/tests/scenario_tests/test_events_ignore_self.py +++ b/tests/scenario_tests/test_events_ignore_self.py @@ -100,6 +100,7 @@ def handle_app_mention(say): "type": "message", "channel": "C111", "ts": "1599529504.000400", + "channel_type": "channel", }, "reaction": "heart_eyes", "item_user": "W111", diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index edc77ecf3..9e1176c74 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -483,7 +483,7 @@ def build_payload(event: dict) -> dict: "user_profile": {}, "thread_ts": "1726133698.626339", "parent_user_id": "W222", - "channel": "D111", + "channel": "C111", "event_ts": "1726133700.887259", "channel_type": "channel", } @@ -518,7 +518,7 @@ def build_payload(event: dict) -> dict: "reply_users": ["U222", "W111"], "is_locked": False, }, - "channel": "D111", + "channel": "C111", "hidden": True, "ts": "1726133701.028300", "event_ts": "1726133701.028300", diff --git a/tests/scenario_tests_async/test_events_ignore_self.py b/tests/scenario_tests_async/test_events_ignore_self.py index 7ec9d0cce..fa398a907 100644 --- a/tests/scenario_tests_async/test_events_ignore_self.py +++ b/tests/scenario_tests_async/test_events_ignore_self.py @@ -89,6 +89,7 @@ async def test_self_events_disabled(self): "type": "message", "channel": "C111", "ts": "1599529504.000400", + "channel_type": "channel", }, "reaction": "heart_eyes", "item_user": "W111", diff --git a/tests/slack_bolt/request/test_payload_utils.py b/tests/slack_bolt/request/test_payload_utils.py new file mode 100644 index 000000000..f1c1d94c6 --- /dev/null +++ b/tests/slack_bolt/request/test_payload_utils.py @@ -0,0 +1,283 @@ +from slack_bolt.request.payload_utils import ( + is_event, + is_user_message_event_in_assistant_thread, + is_bot_message_event_in_assistant_thread, + is_other_message_sub_event_in_assistant_thread, + is_assistant_event, + is_assistant_thread_started_event, + is_assistant_thread_context_changed_event, +) +from tests.scenario_tests.test_events_assistant import ( + build_payload, + thread_started_event_body, + thread_context_changed_event_body, + user_message_event_body, + user_message_event_body_with_assistant_thread, + message_changed_event_body, + channel_user_message_event_body, + channel_message_changed_event_body, +) +from tests.scenario_tests.test_message_bot import ( + bot_message_event_payload, + classic_bot_message_event_payload, +) +from tests.scenario_tests.test_message_deleted import event_payload as message_deleted_channel_body +from tests.scenario_tests.test_events_ignore_self import event_body as reaction_added_event_body +from tests.scenario_tests.test_block_actions import body as block_actions_body + +file_share_im_message_body = build_payload( + { + "user": "W222", + "type": "message", + "subtype": "file_share", + "ts": "1726133700.887259", + "text": "uploaded a file", + "files": [ + { + "id": "F111", + "created": 1726133700, + "name": "test.png", + "title": "test.png", + "mimetype": "image/png", + "filetype": "png", + "user": "W222", + "size": 12345, + "mode": "hosted", + "is_external": False, + "is_public": False, + "url_private": "https://files.slack.com/files-pri/T111-F111/test.png", + "permalink": "https://example.slack.com/files/W222/F111/test.png", + } + ], + "upload": True, + "display_as_bot": False, + "thread_ts": "1726133698.626339", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +bot_im_thread_message_body = build_payload( + { + "type": "message", + "ts": "1726133700.887259", + "text": "Here is your answer", + "user": "UB111", + "bot_id": "B111", + "app_id": "A222", + "bot_profile": { + "id": "B111", + "deleted": False, + "name": "assistant-app", + "updated": 1726133600, + "app_id": "A222", + "team_id": "T111", + }, + "thread_ts": "1726133698.626339", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +im_message_no_thread_ts_body = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "A top-level DM, not in a thread", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +slash_command_body = { + "token": "verification_token", + "command": "/test", + "text": "hello", + "user_id": "U111", + "user_name": "primary-owner", + "channel_id": "C111", + "channel_name": "test-channel", + "team_id": "T111", + "team_domain": "test-domain", + "api_app_id": "A111", + "is_enterprise_install": "false", + "response_url": "https://hooks.slack.com/commands/T111/111/xxx", + "trigger_id": "111.222.xxx", +} + + +class TestPayloadUtils: + def test_is_event(self): + positives = { + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "user_message_im": user_message_event_body, + "user_message_im_with_assistant_thread": user_message_event_body_with_assistant_thread, + "message_changed_im": message_changed_event_body, + "channel_user_message": channel_user_message_event_body, + "channel_message_changed": channel_message_changed_event_body, + "bot_message_channel": bot_message_event_payload, + "classic_bot_message_channel": classic_bot_message_event_payload, + "message_deleted_channel": message_deleted_channel_body, + "reaction_added": reaction_added_event_body, + "file_share_im": file_share_im_message_body, + "bot_im_thread": bot_im_thread_message_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + } + negatives = { + "block_actions": block_actions_body, + "slash_command": slash_command_body, + "empty_dict": {}, + } + for key, body in positives.items(): + assert is_event(body), f"{key} should be recognized as an event" + for key, body in negatives.items(): + assert not is_event(body), f"{key} should NOT be recognized as an event" + + def test_is_user_message_event_in_assistant_thread(self): + # Requires: is_im_message_event + thread_ts present + bot_id absent + positives = { + "user_message_im": user_message_event_body, + "user_message_im_with_assistant_thread": user_message_event_body_with_assistant_thread, + "file_share_im": file_share_im_message_body, + } + negatives = { + "bot_im_thread": bot_im_thread_message_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + "message_changed_im": message_changed_event_body, + "channel_user_message": channel_user_message_event_body, + "channel_message_changed": channel_message_changed_event_body, + "bot_message_channel": bot_message_event_payload, + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + } + for key, body in positives.items(): + assert is_user_message_event_in_assistant_thread( + body + ), f"{key} should pass {is_user_message_event_in_assistant_thread.__name__}" + for key, body in negatives.items(): + assert not is_user_message_event_in_assistant_thread( + body + ), f"{key} should NOT pass {is_user_message_event_in_assistant_thread.__name__}" + + def test_is_bot_message_event_in_assistant_thread(self): + positives = { + "bot_im_thread": bot_im_thread_message_body, + } + negatives = { + "user_message_im": user_message_event_body, + "file_share_im": file_share_im_message_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + "message_changed_im": message_changed_event_body, + "channel_user_message": channel_user_message_event_body, + "bot_message_channel": bot_message_event_payload, + "classic_bot_message_channel": classic_bot_message_event_payload, + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + } + for key, body in positives.items(): + assert is_bot_message_event_in_assistant_thread( + body + ), f"{key} should pass {is_bot_message_event_in_assistant_thread.__name__}" + for key, body in negatives.items(): + assert not is_bot_message_event_in_assistant_thread( + body + ), f"{key} should NOT pass {is_bot_message_event_in_assistant_thread.__name__}" + + def test_is_bot_message_user_message_asymmetry(self): + assert is_user_message_event_in_assistant_thread(file_share_im_message_body) + assert not is_bot_message_event_in_assistant_thread(file_share_im_message_body) + + assert is_bot_message_event_in_assistant_thread(bot_im_thread_message_body) + assert not is_user_message_event_in_assistant_thread(bot_im_thread_message_body) + + def test_is_other_message_sub_event_in_assistant_thread(self): + positives = { + "message_changed_im": message_changed_event_body, + } + negatives = { + "user_message_im": user_message_event_body, + "bot_im_thread": bot_im_thread_message_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + "channel_message_changed": channel_message_changed_event_body, + "channel_user_message": channel_user_message_event_body, + "message_deleted_channel": message_deleted_channel_body, + "thread_started": thread_started_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + } + for key, body in positives.items(): + assert is_other_message_sub_event_in_assistant_thread( + body + ), f"{key} should pass {is_other_message_sub_event_in_assistant_thread.__name__}" + for key, body in negatives.items(): + assert not is_other_message_sub_event_in_assistant_thread( + body + ), f"{key} should NOT pass {is_other_message_sub_event_in_assistant_thread.__name__}" + + def test_is_assistant_event(self): + positives = { + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "user_message_im": user_message_event_body, + "user_message_im_with_assistant_thread": user_message_event_body_with_assistant_thread, + "file_share_im": file_share_im_message_body, + "bot_im_thread": bot_im_thread_message_body, + } + negatives = { + "message_changed_im": message_changed_event_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + "channel_user_message": channel_user_message_event_body, + "channel_message_changed": channel_message_changed_event_body, + "bot_message_channel": bot_message_event_payload, + "classic_bot_message_channel": classic_bot_message_event_payload, + "message_deleted_channel": message_deleted_channel_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + } + for key, body in positives.items(): + assert is_assistant_event(body), f"{key} should pass {is_assistant_event.__name__}" + for key, body in negatives.items(): + assert not is_assistant_event(body), f"{key} should NOT pass {is_assistant_event.__name__}" + + def test_is_assistant_thread_started_event(self): + assert is_assistant_thread_started_event(thread_started_event_body) + + negatives = { + "thread_context_changed": thread_context_changed_event_body, + "user_message_im": user_message_event_body, + "message_changed_im": message_changed_event_body, + "bot_im_thread": bot_im_thread_message_body, + "channel_user_message": channel_user_message_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + } + for key, body in negatives.items(): + assert not is_assistant_thread_started_event( + body + ), f"{key} should NOT pass {is_assistant_thread_started_event.__name__}" + + def test_is_assistant_thread_context_changed_event(self): + assert is_assistant_thread_context_changed_event(thread_context_changed_event_body) + + negatives = { + "thread_started": thread_started_event_body, + "user_message_im": user_message_event_body, + "message_changed_im": message_changed_event_body, + "bot_im_thread": bot_im_thread_message_body, + "channel_user_message": channel_user_message_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + } + for key, body in negatives.items(): + assert not is_assistant_thread_context_changed_event( + body + ), f"{key} should NOT pass {is_assistant_thread_context_changed_event.__name__}" From 256388cc2a32cfb8775bc4a28ae52070b9f6ddff Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 14 Jul 2026 16:19:50 -0400 Subject: [PATCH 262/282] feat: widen set_suggested_prompts initialization scope to any DM (#1549) Co-authored-by: Claude --- .../context/assistant/assistant_utilities.py | 21 --- .../assistant/async_assistant_utilities.py | 21 --- .../async_set_suggested_prompts.py | 7 +- .../set_suggested_prompts.py | 7 +- .../async_attaching_conversation_kwargs.py | 82 +++++++---- .../attaching_conversation_kwargs.py | 84 +++++++---- slack_bolt/request/payload_utils.py | 55 +++++--- .../context/test_set_suggested_prompts.py | 43 ++++++ .../test_attaching_conversation_kwargs.py | 125 +++++++++++++++++ .../slack_bolt/request/test_payload_utils.py | 131 +++++++++++++++++- .../test_async_set_suggested_prompts.py | 60 ++++++++ ...est_async_attaching_conversation_kwargs.py | 130 +++++++++++++++++ 12 files changed, 634 insertions(+), 132 deletions(-) diff --git a/slack_bolt/context/assistant/assistant_utilities.py b/slack_bolt/context/assistant/assistant_utilities.py index 42f05c94b..e9614fd8d 100644 --- a/slack_bolt/context/assistant/assistant_utilities.py +++ b/slack_bolt/context/assistant/assistant_utilities.py @@ -1,4 +1,3 @@ -import warnings from typing import Optional from slack_sdk.web import WebClient @@ -11,8 +10,6 @@ from .internals import has_channel_id_and_thread_ts from ..get_thread_context.get_thread_context import GetThreadContext from ..save_thread_context import SaveThreadContext -from ..set_status import SetStatus -from ..set_suggested_prompts import SetSuggestedPrompts from ..set_title import SetTitle @@ -47,28 +44,10 @@ def __init__( # When moving this code to Bolt internals, no need to raise an exception for this pattern raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})") - def is_valid(self) -> bool: - return self.channel_id is not None and self.thread_ts is not None - - @property - def set_status(self) -> SetStatus: - warnings.warn( - "AssistantUtilities.set_status is deprecated. " - "Use the set_status argument directly in your listener function " - "or access it via context.set_status instead.", - DeprecationWarning, - stacklevel=2, - ) - return SetStatus(self.client, self.channel_id, self.thread_ts) - @property def set_title(self) -> SetTitle: return SetTitle(self.client, self.channel_id, self.thread_ts) - @property - def set_suggested_prompts(self) -> SetSuggestedPrompts: - return SetSuggestedPrompts(self.client, self.channel_id, self.thread_ts) - @property def say(self) -> Say: def build_metadata() -> Optional[dict]: diff --git a/slack_bolt/context/assistant/async_assistant_utilities.py b/slack_bolt/context/assistant/async_assistant_utilities.py index b40b2619c..6acf531ce 100644 --- a/slack_bolt/context/assistant/async_assistant_utilities.py +++ b/slack_bolt/context/assistant/async_assistant_utilities.py @@ -1,4 +1,3 @@ -import warnings from typing import Optional from slack_sdk.web.async_client import AsyncWebClient @@ -14,8 +13,6 @@ from .internals import has_channel_id_and_thread_ts from ..get_thread_context.async_get_thread_context import AsyncGetThreadContext from ..save_thread_context.async_save_thread_context import AsyncSaveThreadContext -from ..set_status.async_set_status import AsyncSetStatus -from ..set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts from ..set_title.async_set_title import AsyncSetTitle @@ -50,28 +47,10 @@ def __init__( # When moving this code to Bolt internals, no need to raise an exception for this pattern raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})") - def is_valid(self) -> bool: - return self.channel_id is not None and self.thread_ts is not None - - @property - def set_status(self) -> AsyncSetStatus: - warnings.warn( - "AsyncAssistantUtilities.set_status is deprecated. " - "Use the set_status argument directly in your listener function " - "or access it via context.set_status instead.", - DeprecationWarning, - stacklevel=2, - ) - return AsyncSetStatus(self.client, self.channel_id, self.thread_ts) - @property def set_title(self) -> AsyncSetTitle: return AsyncSetTitle(self.client, self.channel_id, self.thread_ts) - @property - def set_suggested_prompts(self) -> AsyncSetSuggestedPrompts: - return AsyncSetSuggestedPrompts(self.client, self.channel_id, self.thread_ts) - @property def say(self) -> AsyncSay: return AsyncSay( diff --git a/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py index 2079b6448..68b41858b 100644 --- a/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py +++ b/slack_bolt/context/set_suggested_prompts/async_set_suggested_prompts.py @@ -7,13 +7,13 @@ class AsyncSetSuggestedPrompts: client: AsyncWebClient channel_id: str - thread_ts: str + thread_ts: Optional[str] def __init__( self, client: AsyncWebClient, channel_id: str, - thread_ts: str, + thread_ts: Optional[str] = None, ): self.client = client self.channel_id = channel_id @@ -23,6 +23,7 @@ async def __call__( self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, + thread_ts: Optional[str] = None, ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: @@ -33,7 +34,7 @@ async def __call__( return await self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, - thread_ts=self.thread_ts, + thread_ts=thread_ts if thread_ts is not None else self.thread_ts, prompts=prompts_arg, title=title, ) diff --git a/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py index 21ff815e1..349481df4 100644 --- a/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py +++ b/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py @@ -7,13 +7,13 @@ class SetSuggestedPrompts: client: WebClient channel_id: str - thread_ts: str + thread_ts: Optional[str] def __init__( self, client: WebClient, channel_id: str, - thread_ts: str, + thread_ts: Optional[str] = None, ): self.client = client self.channel_id = channel_id @@ -23,6 +23,7 @@ def __call__( self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, + thread_ts: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: @@ -33,7 +34,7 @@ def __call__( return self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, - thread_ts=self.thread_ts, + thread_ts=thread_ts if thread_ts is not None else self.thread_ts, prompts=prompts_arg, title=title, ) diff --git a/slack_bolt/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.py b/slack_bolt/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.py index 315ec2a50..ab69f5768 100644 --- a/slack_bolt/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.py +++ b/slack_bolt/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.py @@ -4,9 +4,17 @@ from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream from slack_bolt.context.set_status.async_set_status import AsyncSetStatus +from slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts import AsyncSetSuggestedPrompts from slack_bolt.middleware.async_middleware import AsyncMiddleware from slack_bolt.request.async_request import AsyncBoltRequest -from slack_bolt.request.payload_utils import is_assistant_event, to_event +from slack_bolt.request.payload_utils import ( + is_app_home_opened_event, + is_assistant_event, + is_assistant_thread_context_changed_event, + is_assistant_thread_started_event, + is_im_message_event, + to_event, +) from slack_bolt.response import BoltResponse @@ -25,32 +33,48 @@ async def async_process( next: Callable[[], Awaitable[BoltResponse]], ) -> Optional[BoltResponse]: event = to_event(req.body) - if event is not None: - if is_assistant_event(req.body): - assistant = AsyncAssistantUtilities( - payload=event, - context=req.context, - thread_context_store=self.thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - - # TODO: in the future we might want to introduce a "proper" extract_ts utility - thread_ts = req.context.thread_ts or event.get("ts") - if req.context.channel_id and thread_ts: - req.context["set_status"] = AsyncSetStatus( - client=req.context.client, - channel_id=req.context.channel_id, - thread_ts=thread_ts, - ) - req.context["say_stream"] = AsyncSayStream( - client=req.context.client, - channel=req.context.channel_id, - recipient_team_id=req.context.team_id or req.context.enterprise_id, - recipient_user_id=req.context.user_id, - thread_ts=thread_ts, - ) + if event is None: + return await next() + if req.context.channel_id is None: + return await next() + + if is_assistant_event(req.body): + # TODO: eventually we might remove this assistant specific logic + assistant = AsyncAssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_title"] = assistant.set_title + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + + if ( + is_im_message_event(req.body) + or is_assistant_thread_started_event(req.body) + or is_assistant_thread_context_changed_event(req.body) + or is_app_home_opened_event(req.body, tab="messages") + ): + req.context["set_suggested_prompts"] = AsyncSetSuggestedPrompts( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=req.context.thread_ts, + ) + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts_or_ts = req.context.thread_ts or event.get("ts") + if thread_ts_or_ts: + req.context["set_status"] = AsyncSetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts_or_ts, + ) + req.context["say_stream"] = AsyncSayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts_or_ts, + ) return await next() diff --git a/slack_bolt/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.py b/slack_bolt/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.py index 33847fd56..2d6ce7b01 100644 --- a/slack_bolt/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.py +++ b/slack_bolt/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.py @@ -1,11 +1,19 @@ from typing import Optional, Callable -from slack_bolt.context.assistant.assistant_utilities import AssistantUtilities from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore from slack_bolt.context.say_stream.say_stream import SayStream from slack_bolt.context.set_status.set_status import SetStatus +from slack_bolt.context.set_suggested_prompts.set_suggested_prompts import SetSuggestedPrompts from slack_bolt.middleware import Middleware -from slack_bolt.request.payload_utils import is_assistant_event, to_event +from slack_bolt.context.assistant.assistant_utilities import AssistantUtilities +from slack_bolt.request.payload_utils import ( + is_app_home_opened_event, + is_assistant_event, + is_assistant_thread_context_changed_event, + is_assistant_thread_started_event, + is_im_message_event, + to_event, +) from slack_bolt.request.request import BoltRequest from slack_bolt.response.response import BoltResponse @@ -19,32 +27,48 @@ def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: event = to_event(req.body) - if event is not None: - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=event, - context=req.context, - thread_context_store=self.thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context - - # TODO: in the future we might want to introduce a "proper" extract_ts utility - thread_ts = req.context.thread_ts or event.get("ts") - if req.context.channel_id and thread_ts: - req.context["set_status"] = SetStatus( - client=req.context.client, - channel_id=req.context.channel_id, - thread_ts=thread_ts, - ) - req.context["say_stream"] = SayStream( - client=req.context.client, - channel=req.context.channel_id, - recipient_team_id=req.context.team_id or req.context.enterprise_id, - recipient_user_id=req.context.user_id, - thread_ts=thread_ts, - ) + if event is None: + return next() + if req.context.channel_id is None: + return next() + + if is_assistant_event(req.body): + # TODO: eventually we might remove this assistant specific logic + assistant = AssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_title"] = assistant.set_title + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + + if ( + is_im_message_event(req.body) + or is_assistant_thread_started_event(req.body) + or is_assistant_thread_context_changed_event(req.body) + or is_app_home_opened_event(req.body, tab="messages") + ): + req.context["set_suggested_prompts"] = SetSuggestedPrompts( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=req.context.thread_ts, + ) + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts_or_ts = req.context.thread_ts or event.get("ts") + if thread_ts_or_ts: + req.context["set_status"] = SetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts_or_ts, + ) + req.context["say_stream"] = SayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts_or_ts, + ) return next() diff --git a/slack_bolt/request/payload_utils.py b/slack_bolt/request/payload_utils.py index 1ebf70d4f..b74238461 100644 --- a/slack_bolt/request/payload_utils.py +++ b/slack_bolt/request/payload_utils.py @@ -14,7 +14,7 @@ def to_event(body: Dict[str, Any]) -> Optional[Dict[str, Any]]: def to_message(body: Dict[str, Any]) -> Optional[Dict[str, Any]]: - if is_event(body) and body["event"]["type"] == "message": + if is_message_event(body): return to_event(body) return None @@ -31,6 +31,25 @@ def is_workflow_step_execute(body: Dict[str, Any]) -> bool: return is_event(body) and body["event"]["type"] == "workflow_step_execute" and "workflow_step" in body["event"] +def is_message_event(body: Dict[str, Any]) -> bool: + if is_event(body): + return body["event"]["type"] == "message" + return False + + +def is_any_im_message_event(body: Dict[str, Any]) -> bool: + if is_message_event(body): + # Any message event with no subtype or any subtype (message_changed, message_deleted, etc.) + return body["event"].get("channel_type") == "im" + return False + + +def is_im_message_event(body: Dict[str, Any]) -> bool: + if is_any_im_message_event(body): + return body["event"].get("subtype") in (None, "file_share") + return False + + def is_assistant_event(body: Dict[str, Any]) -> bool: return is_event(body) and ( is_assistant_thread_started_event(body) @@ -52,28 +71,24 @@ def is_assistant_thread_context_changed_event(body: Dict[str, Any]) -> bool: return False -def is_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: - if is_event(body): - return body["event"]["type"] == "message" and body["event"].get("channel_type") == "im" +def is_app_home_opened_event(body: Dict[str, Any], tab: Optional[str] = None) -> bool: + if is_event(body) and body["event"]["type"] == "app_home_opened": + if tab is not None: + return body["event"].get("tab") == tab + return True return False def is_user_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: - if is_event(body): - return ( - is_message_event_in_assistant_thread(body) - and body["event"].get("subtype") in (None, "file_share") - and body["event"].get("thread_ts") is not None - and body["event"].get("bot_id") is None - ) + if is_im_message_event(body): + return body["event"].get("thread_ts") is not None and body["event"].get("bot_id") is None return False def is_bot_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: - if is_event(body): + if is_any_im_message_event(body): return ( - is_message_event_in_assistant_thread(body) - and body["event"].get("subtype") is None + body["event"].get("subtype") is None and body["event"].get("thread_ts") is not None and body["event"].get("bot_id") is not None ) @@ -82,14 +97,10 @@ def is_bot_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: def is_other_message_sub_event_in_assistant_thread(body: Dict[str, Any]) -> bool: # message_changed, message_deleted etc. - if is_event(body): - return ( - is_message_event_in_assistant_thread(body) - and not is_user_message_event_in_assistant_thread(body) - and ( - _is_other_message_sub_event(body["event"].get("message")) - or _is_other_message_sub_event(body["event"].get("previous_message")) - ) + if is_any_im_message_event(body): + return not is_user_message_event_in_assistant_thread(body) and ( + _is_other_message_sub_event(body["event"].get("message")) + or _is_other_message_sub_event(body["event"].get("previous_message")) ) return False diff --git a/tests/slack_bolt/context/test_set_suggested_prompts.py b/tests/slack_bolt/context/test_set_suggested_prompts.py index 792b974b5..a743d5656 100644 --- a/tests/slack_bolt/context/test_set_suggested_prompts.py +++ b/tests/slack_bolt/context/test_set_suggested_prompts.py @@ -1,3 +1,5 @@ +from unittest.mock import MagicMock, patch + import pytest from slack_sdk import WebClient from slack_sdk.web import SlackResponse @@ -31,6 +33,47 @@ def test_set_suggested_prompts_objects(self): ) assert response.status_code == 200 + def test_set_suggested_prompts_without_thread_ts(self): + set_suggested_prompts = SetSuggestedPrompts(client=self.web_client, channel_id="C111") + with patch.object( + self.web_client, self.web_client.assistant_threads_setSuggestedPrompts.__name__, return_value=MagicMock() + ) as mock_api: + set_suggested_prompts(prompts=["One", "Two"]) + mock_api.assert_called_once_with( + channel_id="C111", + thread_ts=None, + prompts=[{"title": "One", "message": "One"}, {"title": "Two", "message": "Two"}], + title=None, + ) + + def test_set_suggested_prompts_thread_ts_override(self): + # The call-time thread_ts must win over the stored one + set_suggested_prompts = SetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="999.999") + with patch.object( + self.web_client, self.web_client.assistant_threads_setSuggestedPrompts.__name__, return_value=MagicMock() + ) as mock_api: + set_suggested_prompts(prompts=["One", "Two"], thread_ts="123.123") + mock_api.assert_called_once_with( + channel_id="C111", + thread_ts="123.123", + prompts=[{"title": "One", "message": "One"}, {"title": "Two", "message": "Two"}], + title=None, + ) + + def test_set_suggested_prompts_thread_ts_override_falsy(self): + # An explicitly passed falsy thread_ts must be forwarded, not swallowed by the stored value + set_suggested_prompts = SetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + with patch.object( + self.web_client, self.web_client.assistant_threads_setSuggestedPrompts.__name__, return_value=MagicMock() + ) as mock_api: + set_suggested_prompts(prompts=["One", "Two"], thread_ts="") + mock_api.assert_called_once_with( + channel_id="C111", + thread_ts="", + prompts=[{"title": "One", "message": "One"}, {"title": "Two", "message": "Two"}], + title=None, + ) + def test_set_suggested_prompts_invalid(self): set_suggested_prompts = SetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") with pytest.raises(TypeError): diff --git a/tests/slack_bolt/middleware/attaching_conversation_kwargs/test_attaching_conversation_kwargs.py b/tests/slack_bolt/middleware/attaching_conversation_kwargs/test_attaching_conversation_kwargs.py index b7785eb50..3f46a6b67 100644 --- a/tests/slack_bolt/middleware/attaching_conversation_kwargs/test_attaching_conversation_kwargs.py +++ b/tests/slack_bolt/middleware/attaching_conversation_kwargs/test_attaching_conversation_kwargs.py @@ -4,6 +4,7 @@ from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse from tests.scenario_tests.test_events_assistant import ( + build_payload, thread_started_event_body, user_message_event_body, channel_user_message_event_body, @@ -16,6 +17,70 @@ def next(): ASSISTANT_KWARGS = ("say", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") +# A top-level DM (not in a thread) is not an assistant thread, but set_suggested_prompts is still attached. +top_level_im_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "A top-level DM, not in a thread", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +# A bot-authored top-level DM is also in scope: set_suggested_prompts is attached for any IM message. +bot_im_message_event_body = build_payload( + { + "type": "message", + "ts": "1726133700.887259", + "text": "A DM authored by a bot", + "user": "UB111", + "bot_id": "B111", + "app_id": "A222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +# A file_share DM is in scope too (subtype "file_share" passes is_im_message_event). +file_share_im_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "subtype": "file_share", + "ts": "1726133700.887259", + "text": "uploaded a file", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +# Opening the Messages tab of App Home is in scope for set_suggested_prompts. +app_home_opened_messages_event_body = build_payload( + { + "type": "app_home_opened", + "user": "W222", + "channel": "D111", + "tab": "messages", + "event_ts": "1726133700.887259", + } +) + +# Opening the Home tab is NOT in scope: set_suggested_prompts should not be attached. +app_home_opened_home_event_body = build_payload( + { + "type": "app_home_opened", + "user": "W222", + "channel": "D111", + "tab": "home", + "event_ts": "1726133700.887259", + } +) + class TestAttachingConversationKwargs: def test_assistant_event_attaches_kwargs(self): @@ -46,6 +111,66 @@ def test_user_message_event_attaches_kwargs(self): assert "say_stream" in req.context assert "set_status" in req.context + def test_top_level_dm_attaches_suggested_prompts_but_not_set_title(self): + middleware = AttachingConversationKwargs() + req = BoltRequest(body=top_level_im_message_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + assert "set_title" not in req.context + assert "say" not in req.context + assert "get_thread_context" not in req.context + assert "save_thread_context" not in req.context + assert "say_stream" in req.context + assert "set_status" in req.context + + def test_bot_dm_attaches_suggested_prompts(self): + middleware = AttachingConversationKwargs() + req = BoltRequest(body=bot_im_message_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + + def test_file_share_dm_attaches_suggested_prompts(self): + middleware = AttachingConversationKwargs() + req = BoltRequest(body=file_share_im_message_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + + def test_app_home_opened_messages_tab_attaches_suggested_prompts(self): + middleware = AttachingConversationKwargs() + req = BoltRequest(body=app_home_opened_messages_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + assert "say" not in req.context + assert "set_title" not in req.context + assert "get_thread_context" not in req.context + assert "save_thread_context" not in req.context + + def test_app_home_opened_home_tab_does_not_attach_suggested_prompts(self): + middleware = AttachingConversationKwargs() + req = BoltRequest(body=app_home_opened_home_event_body, mode="socket_mode") + req.context["client"] = WebClient(token="xoxb-test") + + resp = middleware.process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" not in req.context + def test_non_assistant_event_does_not_attach_kwargs(self): middleware = AttachingConversationKwargs() req = BoltRequest(body=channel_user_message_event_body, mode="socket_mode") diff --git a/tests/slack_bolt/request/test_payload_utils.py b/tests/slack_bolt/request/test_payload_utils.py index f1c1d94c6..576cb390d 100644 --- a/tests/slack_bolt/request/test_payload_utils.py +++ b/tests/slack_bolt/request/test_payload_utils.py @@ -1,11 +1,15 @@ from slack_bolt.request.payload_utils import ( is_event, - is_user_message_event_in_assistant_thread, - is_bot_message_event_in_assistant_thread, - is_other_message_sub_event_in_assistant_thread, + is_message_event, + is_any_im_message_event, + is_im_message_event, is_assistant_event, is_assistant_thread_started_event, is_assistant_thread_context_changed_event, + is_app_home_opened_event, + is_user_message_event_in_assistant_thread, + is_bot_message_event_in_assistant_thread, + is_other_message_sub_event_in_assistant_thread, ) from tests.scenario_tests.test_events_assistant import ( build_payload, @@ -93,6 +97,26 @@ } ) +app_home_opened_messages_body = build_payload( + { + "type": "app_home_opened", + "user": "W222", + "channel": "D111", + "tab": "messages", + "event_ts": "1726133700.887259", + } +) + +app_home_opened_home_body = build_payload( + { + "type": "app_home_opened", + "user": "W222", + "channel": "D111", + "tab": "home", + "event_ts": "1726133700.887259", + } +) + slash_command_body = { "token": "verification_token", "command": "/test", @@ -111,6 +135,85 @@ class TestPayloadUtils: + def test_is_message_event(self): + positives = { + "user_message_im": user_message_event_body, + "user_message_im_with_assistant_thread": user_message_event_body_with_assistant_thread, + "message_changed_im": message_changed_event_body, + "channel_user_message": channel_user_message_event_body, + "channel_message_changed": channel_message_changed_event_body, + "bot_message_channel": bot_message_event_payload, + "classic_bot_message_channel": classic_bot_message_event_payload, + "message_deleted_channel": message_deleted_channel_body, + "file_share_im": file_share_im_message_body, + "bot_im_thread": bot_im_thread_message_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + } + negatives = { + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + "slash_command": slash_command_body, + "empty_dict": {}, + } + for key, body in positives.items(): + assert is_message_event(body), f"{key} should be recognized as a message event" + for key, body in negatives.items(): + assert not is_message_event(body), f"{key} should NOT be recognized as a message event" + + def test_is_any_im_message_event(self): + positives = { + "user_message_im": user_message_event_body, + "user_message_im_with_assistant_thread": user_message_event_body_with_assistant_thread, + "message_changed_im": message_changed_event_body, + "file_share_im": file_share_im_message_body, + "bot_im_thread": bot_im_thread_message_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + } + negatives = { + "channel_user_message": channel_user_message_event_body, + "channel_message_changed": channel_message_changed_event_body, + "bot_message_channel": bot_message_event_payload, + "classic_bot_message_channel": classic_bot_message_event_payload, + "message_deleted_channel": message_deleted_channel_body, + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + "slash_command": slash_command_body, + } + for key, body in positives.items(): + assert is_any_im_message_event(body), f"{key} should pass {is_any_im_message_event.__name__}" + for key, body in negatives.items(): + assert not is_any_im_message_event(body), f"{key} should NOT pass {is_any_im_message_event.__name__}" + + def test_is_im_message_event(self): + # subtype must be None or "file_share" to pass + positives = { + "user_message_im": user_message_event_body, + "user_message_im_with_assistant_thread": user_message_event_body_with_assistant_thread, + "file_share_im": file_share_im_message_body, + "bot_im_thread": bot_im_thread_message_body, + "im_no_thread_ts": im_message_no_thread_ts_body, + } + negatives = { + "message_changed_im": message_changed_event_body, + "channel_user_message": channel_user_message_event_body, + "channel_message_changed": channel_message_changed_event_body, + "classic_bot_message_channel": classic_bot_message_event_payload, + "bot_message_channel": bot_message_event_payload, + "message_deleted_channel": message_deleted_channel_body, + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + } + for key, body in positives.items(): + assert is_im_message_event(body), f"{key} should pass {is_im_message_event.__name__}" + for key, body in negatives.items(): + assert not is_im_message_event(body), f"{key} should NOT pass {is_im_message_event.__name__}" + def test_is_event(self): positives = { "thread_started": thread_started_event_body, @@ -281,3 +384,25 @@ def test_is_assistant_thread_context_changed_event(self): assert not is_assistant_thread_context_changed_event( body ), f"{key} should NOT pass {is_assistant_thread_context_changed_event.__name__}" + + def test_is_app_home_opened_event(self): + assert is_app_home_opened_event(app_home_opened_messages_body) + assert is_app_home_opened_event(app_home_opened_home_body) + + assert is_app_home_opened_event(app_home_opened_messages_body, tab="messages") + assert not is_app_home_opened_event(app_home_opened_home_body, tab="messages") + + negatives = { + "thread_started": thread_started_event_body, + "thread_context_changed": thread_context_changed_event_body, + "user_message_im": user_message_event_body, + "channel_user_message": channel_user_message_event_body, + "reaction_added": reaction_added_event_body, + "block_actions": block_actions_body, + "empty_dict": {}, + } + for key, body in negatives.items(): + assert not is_app_home_opened_event(body), f"{key} should NOT pass {is_app_home_opened_event.__name__}" + assert not is_app_home_opened_event( + body, tab="messages" + ), f"{key} should NOT pass {is_app_home_opened_event.__name__} with tab='messages'" diff --git a/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py b/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py index 2a09434a8..7a92ff2a3 100644 --- a/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py +++ b/tests/slack_bolt_async/context/test_async_set_suggested_prompts.py @@ -1,4 +1,5 @@ import asyncio +from unittest.mock import MagicMock, patch import pytest from slack_sdk.web.async_client import AsyncWebClient @@ -41,6 +42,65 @@ async def test_set_suggested_prompts_objects(self): ) assert response.status_code == 200 + @pytest.mark.asyncio + async def test_set_suggested_prompts_without_thread_ts(self): + set_suggested_prompts = AsyncSetSuggestedPrompts(client=self.web_client, channel_id="C111") + mock_api = MagicMock() + + async def fake_api(**kwargs): + return mock_api(**kwargs) + + with patch.object( + self.web_client, self.web_client.assistant_threads_setSuggestedPrompts.__name__, side_effect=fake_api + ): + await set_suggested_prompts(prompts=["One", "Two"]) + mock_api.assert_called_once_with( + channel_id="C111", + thread_ts=None, + prompts=[{"title": "One", "message": "One"}, {"title": "Two", "message": "Two"}], + title=None, + ) + + @pytest.mark.asyncio + async def test_set_suggested_prompts_thread_ts_override(self): + # The call-time thread_ts must win over the stored one + set_suggested_prompts = AsyncSetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="999.999") + mock_api = MagicMock() + + async def fake_api(**kwargs): + return mock_api(**kwargs) + + with patch.object( + self.web_client, self.web_client.assistant_threads_setSuggestedPrompts.__name__, side_effect=fake_api + ): + await set_suggested_prompts(prompts=["One", "Two"], thread_ts="123.123") + mock_api.assert_called_once_with( + channel_id="C111", + thread_ts="123.123", + prompts=[{"title": "One", "message": "One"}, {"title": "Two", "message": "Two"}], + title=None, + ) + + @pytest.mark.asyncio + async def test_set_suggested_prompts_thread_ts_override_falsy(self): + # An explicitly passed falsy thread_ts must be forwarded, not swallowed by the stored value + set_suggested_prompts = AsyncSetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") + mock_api = MagicMock() + + async def fake_api(**kwargs): + return mock_api(**kwargs) + + with patch.object( + self.web_client, self.web_client.assistant_threads_setSuggestedPrompts.__name__, side_effect=fake_api + ): + await set_suggested_prompts(prompts=["One", "Two"], thread_ts="") + mock_api.assert_called_once_with( + channel_id="C111", + thread_ts="", + prompts=[{"title": "One", "message": "One"}, {"title": "Two", "message": "Two"}], + title=None, + ) + @pytest.mark.asyncio async def test_set_suggested_prompts_invalid(self): set_suggested_prompts = AsyncSetSuggestedPrompts(client=self.web_client, channel_id="C111", thread_ts="123.123") diff --git a/tests/slack_bolt_async/middleware/attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py b/tests/slack_bolt_async/middleware/attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py index a00b35cd3..c7da4ce93 100644 --- a/tests/slack_bolt_async/middleware/attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py +++ b/tests/slack_bolt_async/middleware/attaching_conversation_kwargs/test_async_attaching_conversation_kwargs.py @@ -7,6 +7,7 @@ from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse from tests.scenario_tests_async.test_events_assistant import ( + build_payload, thread_started_event_body, user_message_event_body, channel_user_message_event_body, @@ -19,6 +20,70 @@ async def next(): ASSISTANT_KWARGS = ("say", "set_title", "set_suggested_prompts", "get_thread_context", "save_thread_context") +# A top-level DM (not in a thread) is not an assistant thread, but set_suggested_prompts is still attached. +top_level_im_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "A top-level DM, not in a thread", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +# A bot-authored top-level DM is also in scope: set_suggested_prompts is attached for any IM message. +bot_im_message_event_body = build_payload( + { + "type": "message", + "ts": "1726133700.887259", + "text": "A DM authored by a bot", + "user": "UB111", + "bot_id": "B111", + "app_id": "A222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +# A file_share DM is in scope too (subtype "file_share" passes is_im_message_event). +file_share_im_message_event_body = build_payload( + { + "user": "W222", + "type": "message", + "subtype": "file_share", + "ts": "1726133700.887259", + "text": "uploaded a file", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + } +) + +# Opening the Messages tab of App Home is in scope for set_suggested_prompts. +app_home_opened_messages_event_body = build_payload( + { + "type": "app_home_opened", + "user": "W222", + "channel": "D111", + "tab": "messages", + "event_ts": "1726133700.887259", + } +) + +# Opening the Home tab is NOT in scope: set_suggested_prompts should not be attached. +app_home_opened_home_event_body = build_payload( + { + "type": "app_home_opened", + "user": "W222", + "channel": "D111", + "tab": "home", + "event_ts": "1726133700.887259", + } +) + class TestAsyncAttachingConversationKwargs: @pytest.mark.asyncio @@ -51,6 +116,71 @@ async def test_user_message_event_attaches_kwargs(self): assert "say_stream" in req.context assert "set_status" in req.context + @pytest.mark.asyncio + async def test_top_level_dm_attaches_suggested_prompts_but_not_set_title(self): + middleware = AsyncAttachingConversationKwargs() + req = AsyncBoltRequest(body=top_level_im_message_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + assert "set_title" not in req.context + assert "say" not in req.context + assert "get_thread_context" not in req.context + assert "save_thread_context" not in req.context + assert "say_stream" in req.context + assert "set_status" in req.context + + @pytest.mark.asyncio + async def test_bot_dm_attaches_suggested_prompts(self): + middleware = AsyncAttachingConversationKwargs() + req = AsyncBoltRequest(body=bot_im_message_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + + @pytest.mark.asyncio + async def test_file_share_dm_attaches_suggested_prompts(self): + middleware = AsyncAttachingConversationKwargs() + req = AsyncBoltRequest(body=file_share_im_message_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + + @pytest.mark.asyncio + async def test_app_home_opened_messages_tab_attaches_suggested_prompts(self): + middleware = AsyncAttachingConversationKwargs() + req = AsyncBoltRequest(body=app_home_opened_messages_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" in req.context + assert "say" not in req.context + assert "set_title" not in req.context + assert "get_thread_context" not in req.context + assert "save_thread_context" not in req.context + + @pytest.mark.asyncio + async def test_app_home_opened_home_tab_does_not_attach_suggested_prompts(self): + middleware = AsyncAttachingConversationKwargs() + req = AsyncBoltRequest(body=app_home_opened_home_event_body, mode="socket_mode") + req.context["client"] = AsyncWebClient(token="xoxb-test") + + resp = await middleware.async_process(req=req, resp=BoltResponse(status=404), next=next) + + assert resp.status == 200 + assert "set_suggested_prompts" not in req.context + @pytest.mark.asyncio async def test_non_assistant_event_does_not_attach_kwargs(self): middleware = AsyncAttachingConversationKwargs() From a47744cae1cc02dc1cef3f69c0a97d619eee73ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 07:20:01 -0700 Subject: [PATCH 263/282] chore(deps): update pytest requirement from <8.5 to <9.2 (#1540) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/test.txt b/requirements/test.txt index e007e6637..020e2f41a 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,3 +1,3 @@ # pip install -r requirements/test.txt -pytest<8.5 +pytest<9.2 pytest-cov>=7.1.0,<8; python_version>="3.14" # only needed to evaluate coverage on the latest supported python version From 18d438682f76799a0f748402855e41c3dc540372 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:26:08 +0000 Subject: [PATCH 264/282] chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 (#1545) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 74c362f15..89671a50c 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -20,7 +20,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -37,7 +37,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -77,7 +77,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} @@ -144,7 +144,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 1c171f0ff..624243bf6 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.event.release.tag_name || github.ref }} persist-credentials: false From c3eb4c27238eef2a506c40ef4c6f31074fa66910 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 15 Jul 2026 16:44:52 -0400 Subject: [PATCH 265/282] chore(release): version 1.30.0 (#1550) --- docs/reference/async_app.html | 11 +-- .../authorization/authorize_result.html | 4 +- docs/reference/authorization/index.html | 4 +- .../assistant/assistant_utilities.html | 68 -------------- .../assistant/async_assistant_utilities.html | 68 -------------- .../thread_context_store/file/index.html | 2 +- .../async_set_suggested_prompts.html | 11 +-- .../context/set_suggested_prompts/index.html | 11 +-- .../set_suggested_prompts.html | 11 +-- docs/reference/error/index.html | 2 +- docs/reference/index.html | 13 +-- docs/reference/logger/messages.html | 4 +- docs/reference/middleware/async_builtins.html | 70 +++++++++------ .../async_attaching_conversation_kwargs.html | 70 +++++++++------ .../attaching_conversation_kwargs.html | 70 +++++++++------ .../attaching_conversation_kwargs/index.html | 70 +++++++++------ docs/reference/middleware/index.html | 70 +++++++++------ .../reference/oauth/async_oauth_settings.html | 2 +- docs/reference/oauth/oauth_settings.html | 2 +- docs/reference/request/payload_utils.html | 89 ++++++++++++++----- slack_bolt/version.py | 2 +- 21 files changed, 322 insertions(+), 332 deletions(-) diff --git a/docs/reference/async_app.html b/docs/reference/async_app.html index 6a45d2ada..670ba58d0 100644 --- a/docs/reference/async_app.html +++ b/docs/reference/async_app.html @@ -5415,7 +5415,7 @@

            Class variables

      class AsyncSetSuggestedPrompts -(client: slack_sdk.web.async_client.AsyncWebClient,
      channel_id: str,
      thread_ts: str)
      +(client: slack_sdk.web.async_client.AsyncWebClient,
      channel_id: str,
      thread_ts: str | None = None)
      @@ -5425,13 +5425,13 @@

      Class variables

      class AsyncSetSuggestedPrompts:
           client: AsyncWebClient
           channel_id: str
      -    thread_ts: str
      +    thread_ts: Optional[str]
       
           def __init__(
               self,
               client: AsyncWebClient,
               channel_id: str,
      -        thread_ts: str,
      +        thread_ts: Optional[str] = None,
           ):
               self.client = client
               self.channel_id = channel_id
      @@ -5441,6 +5441,7 @@ 

      Class variables

      self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, + thread_ts: Optional[str] = None, ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: @@ -5451,7 +5452,7 @@

      Class variables

      return await self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, - thread_ts=self.thread_ts, + thread_ts=thread_ts if thread_ts is not None else self.thread_ts, prompts=prompts_arg, title=title, )
      @@ -5467,7 +5468,7 @@

      Class variables

      The type of the None singleton.

      -
      var thread_ts : str
      +
      var thread_ts : str | None

      The type of the None singleton.

      diff --git a/docs/reference/authorization/authorize_result.html b/docs/reference/authorization/authorize_result.html index 6eac3724d..d53c5cd5c 100644 --- a/docs/reference/authorization/authorize_result.html +++ b/docs/reference/authorization/authorize_result.html @@ -48,7 +48,7 @@

      Classes

      class AuthorizeResult -(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: str | Sequence[str] | None = None)
      +(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: Sequence[str] | str | None = None)
      @@ -246,7 +246,7 @@

      Class variables

      Static methods

      -def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
      +def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse,
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse | None = None)
      diff --git a/docs/reference/authorization/index.html b/docs/reference/authorization/index.html index 19de311df..2fdd1f916 100644 --- a/docs/reference/authorization/index.html +++ b/docs/reference/authorization/index.html @@ -75,7 +75,7 @@

      Classes

      class AuthorizeResult -(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: str | Sequence[str] | None = None)
      +(*,
      enterprise_id: str | None,
      team_id: str | None,
      team: str | None = None,
      url: str | None = None,
      bot_user_id: str | None = None,
      bot_id: str | None = None,
      bot_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_id: str | None = None,
      user: str | None = None,
      user_token: str | None = None,
      user_scopes: Sequence[str] | str | None = None)
      @@ -273,7 +273,7 @@

      Class variables

      Static methods

      -def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'),
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)
      +def from_auth_test_response(*,
      bot_token: str | None = None,
      user_token: str | None = None,
      bot_scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse,
      user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | AsyncSlackResponse | None = None)
      diff --git a/docs/reference/context/assistant/assistant_utilities.html b/docs/reference/context/assistant/assistant_utilities.html index 40db52284..2200c4f10 100644 --- a/docs/reference/context/assistant/assistant_utilities.html +++ b/docs/reference/context/assistant/assistant_utilities.html @@ -86,28 +86,10 @@

      Classes

      # When moving this code to Bolt internals, no need to raise an exception for this pattern raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})") - def is_valid(self) -> bool: - return self.channel_id is not None and self.thread_ts is not None - - @property - def set_status(self) -> SetStatus: - warnings.warn( - "AssistantUtilities.set_status is deprecated. " - "Use the set_status argument directly in your listener function " - "or access it via context.set_status instead.", - DeprecationWarning, - stacklevel=2, - ) - return SetStatus(self.client, self.channel_id, self.thread_ts) - @property def set_title(self) -> SetTitle: return SetTitle(self.client, self.channel_id, self.thread_ts) - @property - def set_suggested_prompts(self) -> SetSuggestedPrompts: - return SetSuggestedPrompts(self.client, self.channel_id, self.thread_ts) - @property def say(self) -> Say: def build_metadata() -> Optional[dict]: @@ -204,37 +186,6 @@

      Instance variables

      -
      prop set_statusSetStatus
      -
      -
      - -Expand source code - -
      @property
      -def set_status(self) -> SetStatus:
      -    warnings.warn(
      -        "AssistantUtilities.set_status is deprecated. "
      -        "Use the set_status argument directly in your listener function "
      -        "or access it via context.set_status instead.",
      -        DeprecationWarning,
      -        stacklevel=2,
      -    )
      -    return SetStatus(self.client, self.channel_id, self.thread_ts)
      -
      -
      -
      -
      prop set_suggested_promptsSetSuggestedPrompts
      -
      -
      - -Expand source code - -
      @property
      -def set_suggested_prompts(self) -> SetSuggestedPrompts:
      -    return SetSuggestedPrompts(self.client, self.channel_id, self.thread_ts)
      -
      -
      -
      prop set_titleSetTitle
      @@ -248,22 +199,6 @@

      Instance variables

      -

      Methods

      -
      -
      -def is_valid(self) ‑> bool -
      -
      -
      - -Expand source code - -
      def is_valid(self) -> bool:
      -    return self.channel_id is not None and self.thread_ts is not None
      -
      -
      -
      -
      @@ -286,12 +221,9 @@

      channel_id

    • client
    • get_thread_context
    • -
    • is_valid
    • payload
    • save_thread_context
    • say
    • -
    • set_status
    • -
    • set_suggested_prompts
    • set_title
    • thread_context_store
    • thread_ts
    • diff --git a/docs/reference/context/assistant/async_assistant_utilities.html b/docs/reference/context/assistant/async_assistant_utilities.html index fc77b80cb..70f4d0d23 100644 --- a/docs/reference/context/assistant/async_assistant_utilities.html +++ b/docs/reference/context/assistant/async_assistant_utilities.html @@ -86,28 +86,10 @@

      Classes

      # When moving this code to Bolt internals, no need to raise an exception for this pattern raise ValueError(f"Cannot instantiate Assistant for this event pattern ({self.payload})") - def is_valid(self) -> bool: - return self.channel_id is not None and self.thread_ts is not None - - @property - def set_status(self) -> AsyncSetStatus: - warnings.warn( - "AsyncAssistantUtilities.set_status is deprecated. " - "Use the set_status argument directly in your listener function " - "or access it via context.set_status instead.", - DeprecationWarning, - stacklevel=2, - ) - return AsyncSetStatus(self.client, self.channel_id, self.thread_ts) - @property def set_title(self) -> AsyncSetTitle: return AsyncSetTitle(self.client, self.channel_id, self.thread_ts) - @property - def set_suggested_prompts(self) -> AsyncSetSuggestedPrompts: - return AsyncSetSuggestedPrompts(self.client, self.channel_id, self.thread_ts) - @property def say(self) -> AsyncSay: return AsyncSay( @@ -198,37 +180,6 @@

      Instance variables

      -
      prop set_statusAsyncSetStatus
      -
      -
      - -Expand source code - -
      @property
      -def set_status(self) -> AsyncSetStatus:
      -    warnings.warn(
      -        "AsyncAssistantUtilities.set_status is deprecated. "
      -        "Use the set_status argument directly in your listener function "
      -        "or access it via context.set_status instead.",
      -        DeprecationWarning,
      -        stacklevel=2,
      -    )
      -    return AsyncSetStatus(self.client, self.channel_id, self.thread_ts)
      -
      -
      -
      -
      prop set_suggested_promptsAsyncSetSuggestedPrompts
      -
      -
      - -Expand source code - -
      @property
      -def set_suggested_prompts(self) -> AsyncSetSuggestedPrompts:
      -    return AsyncSetSuggestedPrompts(self.client, self.channel_id, self.thread_ts)
      -
      -
      -
      prop set_titleAsyncSetTitle
      @@ -242,22 +193,6 @@

      Instance variables

      -

      Methods

      -
      -
      -def is_valid(self) ‑> bool -
      -
      -
      - -Expand source code - -
      def is_valid(self) -> bool:
      -    return self.channel_id is not None and self.thread_ts is not None
      -
      -
      -
      -
      @@ -280,12 +215,9 @@

      channel_id
    • client
    • get_thread_context
    • -
    • is_valid
    • payload
    • save_thread_context
    • say
    • -
    • set_status
    • -
    • set_suggested_prompts
    • set_title
    • thread_context_store
    • thread_ts
    • diff --git a/docs/reference/context/assistant/thread_context_store/file/index.html b/docs/reference/context/assistant/thread_context_store/file/index.html index cbb4e4db6..4a5d944e1 100644 --- a/docs/reference/context/assistant/thread_context_store/file/index.html +++ b/docs/reference/context/assistant/thread_context_store/file/index.html @@ -48,7 +48,7 @@

      Classes

      class FileAssistantThreadContextStore -(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts')
      diff --git a/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html index 4feda52ba..1c7656456 100644 --- a/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html +++ b/docs/reference/context/set_suggested_prompts/async_set_suggested_prompts.html @@ -48,7 +48,7 @@

      Classes

      class AsyncSetSuggestedPrompts -(client: slack_sdk.web.async_client.AsyncWebClient,
      channel_id: str,
      thread_ts: str)
      +(client: slack_sdk.web.async_client.AsyncWebClient,
      channel_id: str,
      thread_ts: str | None = None)
      @@ -58,13 +58,13 @@

      Classes

      class AsyncSetSuggestedPrompts:
           client: AsyncWebClient
           channel_id: str
      -    thread_ts: str
      +    thread_ts: Optional[str]
       
           def __init__(
               self,
               client: AsyncWebClient,
               channel_id: str,
      -        thread_ts: str,
      +        thread_ts: Optional[str] = None,
           ):
               self.client = client
               self.channel_id = channel_id
      @@ -74,6 +74,7 @@ 

      Classes

      self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, + thread_ts: Optional[str] = None, ) -> AsyncSlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: @@ -84,7 +85,7 @@

      Classes

      return await self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, - thread_ts=self.thread_ts, + thread_ts=thread_ts if thread_ts is not None else self.thread_ts, prompts=prompts_arg, title=title, )
      @@ -100,7 +101,7 @@

      Class variables

      The type of the None singleton.

      -
      var thread_ts : str
      +
      var thread_ts : str | None

      The type of the None singleton.

      diff --git a/docs/reference/context/set_suggested_prompts/index.html b/docs/reference/context/set_suggested_prompts/index.html index 12d864dde..cf606ae2f 100644 --- a/docs/reference/context/set_suggested_prompts/index.html +++ b/docs/reference/context/set_suggested_prompts/index.html @@ -59,7 +59,7 @@

      Classes

      class SetSuggestedPrompts -(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.client.WebClient,
      channel_id: str,
      thread_ts: str | None = None)
      @@ -69,13 +69,13 @@

      Classes

      class SetSuggestedPrompts:
           client: WebClient
           channel_id: str
      -    thread_ts: str
      +    thread_ts: Optional[str]
       
           def __init__(
               self,
               client: WebClient,
               channel_id: str,
      -        thread_ts: str,
      +        thread_ts: Optional[str] = None,
           ):
               self.client = client
               self.channel_id = channel_id
      @@ -85,6 +85,7 @@ 

      Classes

      self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, + thread_ts: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: @@ -95,7 +96,7 @@

      Classes

      return self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, - thread_ts=self.thread_ts, + thread_ts=thread_ts if thread_ts is not None else self.thread_ts, prompts=prompts_arg, title=title, )
      @@ -111,7 +112,7 @@

      Class variables

      The type of the None singleton.

      -
      var thread_ts : str
      +
      var thread_ts : str | None

      The type of the None singleton.

      diff --git a/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html b/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html index 6c0385e57..f034fc677 100644 --- a/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html +++ b/docs/reference/context/set_suggested_prompts/set_suggested_prompts.html @@ -48,7 +48,7 @@

      Classes

      class SetSuggestedPrompts -(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.client.WebClient,
      channel_id: str,
      thread_ts: str | None = None)
      @@ -58,13 +58,13 @@

      Classes

      class SetSuggestedPrompts:
           client: WebClient
           channel_id: str
      -    thread_ts: str
      +    thread_ts: Optional[str]
       
           def __init__(
               self,
               client: WebClient,
               channel_id: str,
      -        thread_ts: str,
      +        thread_ts: Optional[str] = None,
           ):
               self.client = client
               self.channel_id = channel_id
      @@ -74,6 +74,7 @@ 

      Classes

      self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, + thread_ts: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: @@ -84,7 +85,7 @@

      Classes

      return self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, - thread_ts=self.thread_ts, + thread_ts=thread_ts if thread_ts is not None else self.thread_ts, prompts=prompts_arg, title=title, )
      @@ -100,7 +101,7 @@

      Class variables

      The type of the None singleton.

      -
      var thread_ts : str
      +
      var thread_ts : str | None

      The type of the None singleton.

      diff --git a/docs/reference/error/index.html b/docs/reference/error/index.html index f57d690e9..9a9998e63 100644 --- a/docs/reference/error/index.html +++ b/docs/reference/error/index.html @@ -72,7 +72,7 @@

      Subclasses

      class BoltUnhandledRequestError -(*,
      request: ForwardRef('BoltRequest') | ForwardRef('AsyncBoltRequest'),
      current_response: ForwardRef('BoltResponse') | None,
      last_global_middleware_name: str | None = None)
      +(*,
      request: BoltRequest | AsyncBoltRequest,
      current_response: BoltResponse | None,
      last_global_middleware_name: str | None = None)
      diff --git a/docs/reference/index.html b/docs/reference/index.html index 70d84875a..ac1666851 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -5337,7 +5337,7 @@

      Returns

      class FileAssistantThreadContextStore -(base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts') +(base_dir: str = '/Users/wbergamin/.bolt-app-assistant-thread-contexts')
      @@ -6039,7 +6039,7 @@

      Class variables

      class SetSuggestedPrompts -(client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str) +(client: slack_sdk.web.client.WebClient,
      channel_id: str,
      thread_ts: str | None = None)
      @@ -6049,13 +6049,13 @@

      Class variables

      class SetSuggestedPrompts:
           client: WebClient
           channel_id: str
      -    thread_ts: str
      +    thread_ts: Optional[str]
       
           def __init__(
               self,
               client: WebClient,
               channel_id: str,
      -        thread_ts: str,
      +        thread_ts: Optional[str] = None,
           ):
               self.client = client
               self.channel_id = channel_id
      @@ -6065,6 +6065,7 @@ 

      Class variables

      self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, + thread_ts: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: @@ -6075,7 +6076,7 @@

      Class variables

      return self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, - thread_ts=self.thread_ts, + thread_ts=thread_ts if thread_ts is not None else self.thread_ts, prompts=prompts_arg, title=title, )
      @@ -6091,7 +6092,7 @@

      Class variables

      The type of the None singleton.

      -
      var thread_ts : str
      +
      var thread_ts : str | None

      The type of the None singleton.

      diff --git a/docs/reference/logger/messages.html b/docs/reference/logger/messages.html index 1072e6479..e69b45fc9 100644 --- a/docs/reference/logger/messages.html +++ b/docs/reference/logger/messages.html @@ -409,7 +409,7 @@

      Functions

      -def warning_unhandled_by_global_middleware(name: str,
      req: BoltRequest | ForwardRef('AsyncBoltRequest')) ‑> str
      +def warning_unhandled_by_global_middleware(name: str,
      req: BoltRequest | AsyncBoltRequest) ‑> str
      @@ -427,7 +427,7 @@

      Functions

      -def warning_unhandled_request(req: BoltRequest | ForwardRef('AsyncBoltRequest')) ‑> str +def warning_unhandled_request(req: BoltRequest | AsyncBoltRequest) ‑> str
      diff --git a/docs/reference/middleware/async_builtins.html b/docs/reference/middleware/async_builtins.html index 1ddea9222..8f7b1ba4f 100644 --- a/docs/reference/middleware/async_builtins.html +++ b/docs/reference/middleware/async_builtins.html @@ -70,34 +70,50 @@

      Classes

      next: Callable[[], Awaitable[BoltResponse]], ) -> Optional[BoltResponse]: event = to_event(req.body) - if event is not None: - if is_assistant_event(req.body): - assistant = AsyncAssistantUtilities( - payload=event, - context=req.context, - thread_context_store=self.thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context + if event is None: + return await next() + if req.context.channel_id is None: + return await next() + + if is_assistant_event(req.body): + # TODO: eventually we might remove this assistant specific logic + assistant = AsyncAssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_title"] = assistant.set_title + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + + if ( + is_im_message_event(req.body) + or is_assistant_thread_started_event(req.body) + or is_assistant_thread_context_changed_event(req.body) + or is_app_home_opened_event(req.body, tab="messages") + ): + req.context["set_suggested_prompts"] = AsyncSetSuggestedPrompts( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=req.context.thread_ts, + ) - # TODO: in the future we might want to introduce a "proper" extract_ts utility - thread_ts = req.context.thread_ts or event.get("ts") - if req.context.channel_id and thread_ts: - req.context["set_status"] = AsyncSetStatus( - client=req.context.client, - channel_id=req.context.channel_id, - thread_ts=thread_ts, - ) - req.context["say_stream"] = AsyncSayStream( - client=req.context.client, - channel=req.context.channel_id, - recipient_team_id=req.context.team_id or req.context.enterprise_id, - recipient_user_id=req.context.user_id, - thread_ts=thread_ts, - ) + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts_or_ts = req.context.thread_ts or event.get("ts") + if thread_ts_or_ts: + req.context["set_status"] = AsyncSetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts_or_ts, + ) + req.context["say_stream"] = AsyncSayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts_or_ts, + ) return await next()

      A middleware can process request data before other middleware and listener functions.

      diff --git a/docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html b/docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html index a0f5bdf85..e2bbe7045 100644 --- a/docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html +++ b/docs/reference/middleware/attaching_conversation_kwargs/async_attaching_conversation_kwargs.html @@ -70,34 +70,50 @@

      Classes

      next: Callable[[], Awaitable[BoltResponse]], ) -> Optional[BoltResponse]: event = to_event(req.body) - if event is not None: - if is_assistant_event(req.body): - assistant = AsyncAssistantUtilities( - payload=event, - context=req.context, - thread_context_store=self.thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context + if event is None: + return await next() + if req.context.channel_id is None: + return await next() - # TODO: in the future we might want to introduce a "proper" extract_ts utility - thread_ts = req.context.thread_ts or event.get("ts") - if req.context.channel_id and thread_ts: - req.context["set_status"] = AsyncSetStatus( - client=req.context.client, - channel_id=req.context.channel_id, - thread_ts=thread_ts, - ) - req.context["say_stream"] = AsyncSayStream( - client=req.context.client, - channel=req.context.channel_id, - recipient_team_id=req.context.team_id or req.context.enterprise_id, - recipient_user_id=req.context.user_id, - thread_ts=thread_ts, - ) + if is_assistant_event(req.body): + # TODO: eventually we might remove this assistant specific logic + assistant = AsyncAssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_title"] = assistant.set_title + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + + if ( + is_im_message_event(req.body) + or is_assistant_thread_started_event(req.body) + or is_assistant_thread_context_changed_event(req.body) + or is_app_home_opened_event(req.body, tab="messages") + ): + req.context["set_suggested_prompts"] = AsyncSetSuggestedPrompts( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=req.context.thread_ts, + ) + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts_or_ts = req.context.thread_ts or event.get("ts") + if thread_ts_or_ts: + req.context["set_status"] = AsyncSetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts_or_ts, + ) + req.context["say_stream"] = AsyncSayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts_or_ts, + ) return await next()

      A middleware can process request data before other middleware and listener functions.

      diff --git a/docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html b/docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html index 8a1911323..e9d558fec 100644 --- a/docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html +++ b/docs/reference/middleware/attaching_conversation_kwargs/attaching_conversation_kwargs.html @@ -64,34 +64,50 @@

      Classes

      def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: event = to_event(req.body) - if event is not None: - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=event, - context=req.context, - thread_context_store=self.thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context + if event is None: + return next() + if req.context.channel_id is None: + return next() - # TODO: in the future we might want to introduce a "proper" extract_ts utility - thread_ts = req.context.thread_ts or event.get("ts") - if req.context.channel_id and thread_ts: - req.context["set_status"] = SetStatus( - client=req.context.client, - channel_id=req.context.channel_id, - thread_ts=thread_ts, - ) - req.context["say_stream"] = SayStream( - client=req.context.client, - channel=req.context.channel_id, - recipient_team_id=req.context.team_id or req.context.enterprise_id, - recipient_user_id=req.context.user_id, - thread_ts=thread_ts, - ) + if is_assistant_event(req.body): + # TODO: eventually we might remove this assistant specific logic + assistant = AssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_title"] = assistant.set_title + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + + if ( + is_im_message_event(req.body) + or is_assistant_thread_started_event(req.body) + or is_assistant_thread_context_changed_event(req.body) + or is_app_home_opened_event(req.body, tab="messages") + ): + req.context["set_suggested_prompts"] = SetSuggestedPrompts( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=req.context.thread_ts, + ) + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts_or_ts = req.context.thread_ts or event.get("ts") + if thread_ts_or_ts: + req.context["set_status"] = SetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts_or_ts, + ) + req.context["say_stream"] = SayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts_or_ts, + ) return next()

      A middleware can process request data before other middleware and listener functions.

      diff --git a/docs/reference/middleware/attaching_conversation_kwargs/index.html b/docs/reference/middleware/attaching_conversation_kwargs/index.html index 308a52712..38da4442e 100644 --- a/docs/reference/middleware/attaching_conversation_kwargs/index.html +++ b/docs/reference/middleware/attaching_conversation_kwargs/index.html @@ -75,34 +75,50 @@

      Classes

      def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: event = to_event(req.body) - if event is not None: - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=event, - context=req.context, - thread_context_store=self.thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context + if event is None: + return next() + if req.context.channel_id is None: + return next() - # TODO: in the future we might want to introduce a "proper" extract_ts utility - thread_ts = req.context.thread_ts or event.get("ts") - if req.context.channel_id and thread_ts: - req.context["set_status"] = SetStatus( - client=req.context.client, - channel_id=req.context.channel_id, - thread_ts=thread_ts, - ) - req.context["say_stream"] = SayStream( - client=req.context.client, - channel=req.context.channel_id, - recipient_team_id=req.context.team_id or req.context.enterprise_id, - recipient_user_id=req.context.user_id, - thread_ts=thread_ts, - ) + if is_assistant_event(req.body): + # TODO: eventually we might remove this assistant specific logic + assistant = AssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_title"] = assistant.set_title + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + + if ( + is_im_message_event(req.body) + or is_assistant_thread_started_event(req.body) + or is_assistant_thread_context_changed_event(req.body) + or is_app_home_opened_event(req.body, tab="messages") + ): + req.context["set_suggested_prompts"] = SetSuggestedPrompts( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=req.context.thread_ts, + ) + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts_or_ts = req.context.thread_ts or event.get("ts") + if thread_ts_or_ts: + req.context["set_status"] = SetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts_or_ts, + ) + req.context["say_stream"] = SayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts_or_ts, + ) return next()

      A middleware can process request data before other middleware and listener functions.

      diff --git a/docs/reference/middleware/index.html b/docs/reference/middleware/index.html index 9f2053a5d..153342bc1 100644 --- a/docs/reference/middleware/index.html +++ b/docs/reference/middleware/index.html @@ -136,34 +136,50 @@

      Classes

      def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: event = to_event(req.body) - if event is not None: - if is_assistant_event(req.body): - assistant = AssistantUtilities( - payload=event, - context=req.context, - thread_context_store=self.thread_context_store, - ) - req.context["say"] = assistant.say - req.context["set_title"] = assistant.set_title - req.context["set_suggested_prompts"] = assistant.set_suggested_prompts - req.context["get_thread_context"] = assistant.get_thread_context - req.context["save_thread_context"] = assistant.save_thread_context + if event is None: + return next() + if req.context.channel_id is None: + return next() - # TODO: in the future we might want to introduce a "proper" extract_ts utility - thread_ts = req.context.thread_ts or event.get("ts") - if req.context.channel_id and thread_ts: - req.context["set_status"] = SetStatus( - client=req.context.client, - channel_id=req.context.channel_id, - thread_ts=thread_ts, - ) - req.context["say_stream"] = SayStream( - client=req.context.client, - channel=req.context.channel_id, - recipient_team_id=req.context.team_id or req.context.enterprise_id, - recipient_user_id=req.context.user_id, - thread_ts=thread_ts, - ) + if is_assistant_event(req.body): + # TODO: eventually we might remove this assistant specific logic + assistant = AssistantUtilities( + payload=event, + context=req.context, + thread_context_store=self.thread_context_store, + ) + req.context["say"] = assistant.say + req.context["set_title"] = assistant.set_title + req.context["get_thread_context"] = assistant.get_thread_context + req.context["save_thread_context"] = assistant.save_thread_context + + if ( + is_im_message_event(req.body) + or is_assistant_thread_started_event(req.body) + or is_assistant_thread_context_changed_event(req.body) + or is_app_home_opened_event(req.body, tab="messages") + ): + req.context["set_suggested_prompts"] = SetSuggestedPrompts( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=req.context.thread_ts, + ) + + # TODO: in the future we might want to introduce a "proper" extract_ts utility + thread_ts_or_ts = req.context.thread_ts or event.get("ts") + if thread_ts_or_ts: + req.context["set_status"] = SetStatus( + client=req.context.client, + channel_id=req.context.channel_id, + thread_ts=thread_ts_or_ts, + ) + req.context["say_stream"] = SayStream( + client=req.context.client, + channel=req.context.channel_id, + recipient_team_id=req.context.team_id or req.context.enterprise_id, + recipient_user_id=req.context.user_id, + thread_ts=thread_ts_or_ts, + ) return next()

      A middleware can process request data before other middleware and listener functions.

      diff --git a/docs/reference/oauth/async_oauth_settings.html b/docs/reference/oauth/async_oauth_settings.html index 5e6a543c4..3b8c04edb 100644 --- a/docs/reference/oauth/async_oauth_settings.html +++ b/docs/reference/oauth/async_oauth_settings.html @@ -48,7 +48,7 @@

      Classes

      class AsyncOAuthSettings -(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: AsyncCallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.async_oauth_settings (WARNING)>)
      +(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: AsyncCallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.async_oauth_settings (WARNING)>)
      diff --git a/docs/reference/oauth/oauth_settings.html b/docs/reference/oauth/oauth_settings.html index 1eb2ab7dd..cd8def497 100644 --- a/docs/reference/oauth/oauth_settings.html +++ b/docs/reference/oauth/oauth_settings.html @@ -48,7 +48,7 @@

      Classes

      class OAuthSettings -(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: str | Sequence[str] | None = None,
      user_scopes: str | Sequence[str] | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: CallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.state_store.OAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.oauth_settings (WARNING)>)
      +(*,
      client_id: str | None = None,
      client_secret: str | None = None,
      scopes: Sequence[str] | str | None = None,
      user_scopes: Sequence[str] | str | None = None,
      redirect_uri: str | None = None,
      install_path: str = '/slack/install',
      install_page_rendering_enabled: bool = True,
      redirect_uri_path: str = '/slack/oauth_redirect',
      callback_options: CallbackOptions | None = None,
      success_url: str | None = None,
      failure_url: str | None = None,
      authorization_url: str | None = None,
      installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None,
      installation_store_bot_only: bool = False,
      token_rotation_expiration_minutes: int = 120,
      user_token_resolution: str = 'authed_user',
      state_validation_enabled: bool = True,
      state_store: slack_sdk.oauth.state_store.state_store.OAuthStateStore | None = None,
      state_cookie_name: str = 'slack-app-oauth-state',
      state_expiration_seconds: int = 600,
      logger: logging.Logger = <Logger slack_bolt.oauth.oauth_settings (WARNING)>)
      diff --git a/docs/reference/request/payload_utils.html b/docs/reference/request/payload_utils.html index 4fe75fd81..b583c3a51 100644 --- a/docs/reference/request/payload_utils.html +++ b/docs/reference/request/payload_utils.html @@ -63,6 +63,39 @@

      Functions

      +
      +def is_any_im_message_event(body: Dict[str, Any]) ‑> bool +
      +
      +
      + +Expand source code + +
      def is_any_im_message_event(body: Dict[str, Any]) -> bool:
      +    if is_message_event(body):
      +        # Any message event with no subtype or any subtype (message_changed, message_deleted, etc.)
      +        return body["event"].get("channel_type") == "im"
      +    return False
      +
      +
      +
      +
      +def is_app_home_opened_event(body: Dict[str, Any], tab: str | None = None) ‑> bool +
      +
      +
      + +Expand source code + +
      def is_app_home_opened_event(body: Dict[str, Any], tab: Optional[str] = None) -> bool:
      +    if is_event(body) and body["event"]["type"] == "app_home_opened":
      +        if tab is not None:
      +            return body["event"].get("tab") == tab
      +        return True
      +    return False
      +
      +
      +
      def is_assistant_event(body: Dict[str, Any]) ‑> bool
      @@ -159,10 +192,9 @@

      Functions

      Expand source code
      def is_bot_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool:
      -    if is_event(body):
      +    if is_any_im_message_event(body):
               return (
      -            is_message_event_in_assistant_thread(body)
      -            and body["event"].get("subtype") is None
      +            body["event"].get("subtype") is None
                   and body["event"].get("thread_ts") is not None
                   and body["event"].get("bot_id") is not None
               )
      @@ -248,17 +280,32 @@ 

      Functions

      -
      -def is_message_event_in_assistant_thread(body: Dict[str, Any]) ‑> bool +
      +def is_im_message_event(body: Dict[str, Any]) ‑> bool +
      +
      +
      + +Expand source code + +
      def is_im_message_event(body: Dict[str, Any]) -> bool:
      +    if is_any_im_message_event(body):
      +        return body["event"].get("subtype") in (None, "file_share")
      +    return False
      +
      +
      +
      +
      +def is_message_event(body: Dict[str, Any]) ‑> bool
      Expand source code -
      def is_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool:
      +
      def is_message_event(body: Dict[str, Any]) -> bool:
           if is_event(body):
      -        return body["event"]["type"] == "message" and body["event"].get("channel_type") == "im"
      +        return body["event"]["type"] == "message"
           return False
      @@ -299,14 +346,10 @@

      Functions

      def is_other_message_sub_event_in_assistant_thread(body: Dict[str, Any]) -> bool:
           # message_changed, message_deleted etc.
      -    if is_event(body):
      -        return (
      -            is_message_event_in_assistant_thread(body)
      -            and not is_user_message_event_in_assistant_thread(body)
      -            and (
      -                _is_other_message_sub_event(body["event"].get("message"))
      -                or _is_other_message_sub_event(body["event"].get("previous_message"))
      -            )
      +    if is_any_im_message_event(body):
      +        return not is_user_message_event_in_assistant_thread(body) and (
      +            _is_other_message_sub_event(body["event"].get("message"))
      +            or _is_other_message_sub_event(body["event"].get("previous_message"))
               )
           return False
      @@ -347,13 +390,8 @@

      Functions

      Expand source code
      def is_user_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool:
      -    if is_event(body):
      -        return (
      -            is_message_event_in_assistant_thread(body)
      -            and body["event"].get("subtype") in (None, "file_share")
      -            and body["event"].get("thread_ts") is not None
      -            and body["event"].get("bot_id") is None
      -        )
      +    if is_im_message_event(body):
      +        return body["event"].get("thread_ts") is not None and body["event"].get("bot_id") is None
           return False
      @@ -491,7 +529,7 @@

      Functions

      Expand source code
      def to_message(body: Dict[str, Any]) -> Optional[Dict[str, Any]]:
      -    if is_event(body) and body["event"]["type"] == "message":
      +    if is_message_event(body):
               return to_event(body)
           return None
      @@ -582,6 +620,8 @@

      Functions

    • Functions

      • is_action
      • +
      • is_any_im_message_event
      • +
      • is_app_home_opened_event
      • is_assistant_event
      • is_assistant_thread_context_changed_event
      • is_assistant_thread_started_event
      • @@ -595,7 +635,8 @@

        Functions

      • is_event
      • is_function
      • is_global_shortcut
      • -
      • is_message_event_in_assistant_thread
      • +
      • is_im_message_event
      • +
      • is_message_event
      • is_message_shortcut
      • is_options
      • is_other_message_sub_event_in_assistant_thread
      • diff --git a/slack_bolt/version.py b/slack_bolt/version.py index 79018b9b2..2c08c0adb 100644 --- a/slack_bolt/version.py +++ b/slack_bolt/version.py @@ -1,3 +1,3 @@ """Check the latest version at https://pypi.org/project/slack-bolt/""" -__version__ = "1.29.0" +__version__ = "1.30.0" From 52f44443b0e72a898c229e5c4e191f97b05977f8 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 17 Jul 2026 12:57:49 -0400 Subject: [PATCH 266/282] docs(deps): establish and apply a managing-dependencies convention (#1551) Co-authored-by: Claude --- .claude/skills/managing-dependencies/SKILL.md | 127 ++++++++++++++++++ AGENTS.md | 2 + requirements/adapter_dev.txt | 95 +++++++++---- requirements/async_dev.txt | 9 +- requirements/dev_tools.txt | 7 + requirements/test.txt | 7 +- requirements/test_adapter.txt | 12 +- requirements/test_async.txt | 11 +- 8 files changed, 234 insertions(+), 36 deletions(-) create mode 100644 .claude/skills/managing-dependencies/SKILL.md diff --git a/.claude/skills/managing-dependencies/SKILL.md b/.claude/skills/managing-dependencies/SKILL.md new file mode 100644 index 000000000..8f67fccd8 --- /dev/null +++ b/.claude/skills/managing-dependencies/SKILL.md @@ -0,0 +1,127 @@ +--- +name: managing-dependencies +description: >- + Use when adding, updating, pinning, or reviewing any dependency in this repo's requirements/*.txt files, including taking a Dependabot bump or writing a requirement line by hand. Also use when a pip install fails on the older Python jobs while newer ones pass, or when CI errors with "Could not find a version that satisfies", "No matching distribution found", "Requires-Python >=3.x", or "ResolutionImpossible". Triggers include: "add to requirements", "bump/update ", "pin ", "fix this dependabot PR", "CI can't install on Python 3.7/3.8/3.9", "requires-python error in the install step". This skill defines the layout and version-constraint conventions to follow; reach for it before hand-editing any requirements/*.txt file. +--- + +# Managing dependencies + +## Overview + +Every `requirements/*.txt` file in this repo follows one layout convention and one version-constraint pattern. This keeps a single set of pins working across the whole Python matrix and every diff readable. The mechanism for making one line behave differently per interpreter is [PEP 508](https://peps.python.org/pep-0508/) environment markers. That spec is the authority for the marker syntax used throughout this skill. + +Two facts about this repo drive everything below: + +- It supports **Python `>=3.7`** (`requires-python` and the classifiers in `pyproject.toml`). Its CI matrix in `.github/workflows/ci-build.yml` tests **CPython 3.7 through 3.14 only** — there is **no PyPy** (the classifiers list only `Programming Language :: Python :: Implementation :: CPython`). +- Many popular packages keep raising their minimum Python. A bump that raises a dependency's **lower bound** to a release requiring a newer Python makes pip's resolver find nothing installable on the old interpreters. The install step then fails there before tests even run. + +The convention resolves this without dropping old-Python support: pin each interpreter to the newest release it can actually install, using PEP 508 `python_version` markers. + +## File-layout convention + +Each file starts with a `# pip install -r requirements/.txt` header, then lists **one dependency per section**: the name of the dependency (as a `# name` header), an optional rationale note (starting with `# Note:`, explaining why a version is pinned or split), then the requirement line(s), separated from the next section by a blank line. This makes every pin self-documenting. + +``` +# pip install -r requirements/test.txt + +# pytest +pytest<9.2 + +# pytest-cov +# Note: only needed to evaluate coverage on the latest supported python version +pytest-cov>=7.1.0,<8; python_version >= "3.14" +``` + +Keep this layout when adding or editing dependencies. Never leave an empty trailing `;` (a fossil of a collapsed split; delete it — the old `pytest-asyncio<2;` line was exactly this). + +## Which files need Python-version markers + +A marker split is only needed for requirements files installed across the **full** Python matrix. Which file you are editing decides this. To see where a file is installed, read `.github/workflows/ci-build.yml`. It is the source of truth for which Python versions install which requirements files. Everything except `dev_tools.txt` is installed by the `unittest` matrix job across 3.7–3.14. + +| File | Installed on | Needs markers? | +| ----------------------------- | ----------------------------------------------------- | ----------------------------------- | +| `requirements/adapter_dev.txt` | full matrix (`unittest`; also `typecheck`/`codecov` @3.14) | **Yes, if a bump raises the floor** | +| `requirements/async_dev.txt` | full matrix (`unittest`; also `typecheck`/`codecov` @3.14) | **Yes, if a bump raises the floor** | +| `requirements/test.txt` | full matrix (`unittest`) | **Yes, if a bump raises the floor** | +| `requirements/test_adapter.txt` | full matrix (`unittest`; `codecov` @3.14) | **Yes, if a bump raises the floor** | +| `requirements/test_async.txt` | full matrix (`unittest`) | **Yes, if a bump raises the floor** | +| `requirements/dev_tools.txt` | `lint` + `typecheck`, **latest Python only** (3.14) | No, just take the bump (`==` pins) | + +If the bump lands in a latest-Python-only file, take it as-is: no markers, no ceiling, just the layout convention above. + +Unlike some sibling repos, bolt-python has **no requirements file that feeds packaged wheel metadata**: `pyproject.toml` has no `[project.optional-dependencies]`, and `[tool.setuptools.dynamic]` resolves only `version` and `readme`. So there is no "special" file whose comments leak into a wheel — every `requirements/*.txt` file is dev/test-only. + +## The version-constraint pattern + +When a dependency's floor rises to a release that requires a newer Python, **do not** just take the bump, and **do not** drop old-Python support to make CI pass. Instead, split the requirement into `python_version`-marked lines that **partition the whole matrix**. Every interpreter matches exactly one line. Old interpreters keep the last compatible release (with an explicit ceiling), and the newest line is open-ended so future Pythons stay covered. + +``` +# aiohttp +aiohttp>=3,<4; python_version < "3.9" +aiohttp>=3.13.5,<4; python_version >= "3.9" +``` + +`aiohttp` 3.13.5 requires Python `>=3.9`, so Python 3.7/3.8 stay on the older line. The same 3.9 split appears for `falcon`, `fastapi`, `Flask`, `Werkzeug`, `starlette`, `tornado`, `websocket_client`, and (in `test_async.txt`) `asgiref`. The `Django` split lands at 3.8 instead — Django 4.x requires `>=3.8`, so 3.7 keeps the 3.2 line: + +``` +# Django +Django>=3.2,<4; python_version < "3.8" +Django>=4.2.30,<6; python_version >= "3.8" +``` + +## Canonical marker style + +Consistency matters because these lines are read and edited often, and a stray style makes diffs noisy. Standardize on this: + +- Spaces around every operator in the marker: `python_version >= "3.9"`, never `python_version>="3.9"`. +- Double-quoted `major.minor` string: `"3.9"`. (`packaging` compares these version-aware, so `python_version >= "3.9"` correctly includes 3.10–3.14, no lexicographic surprise.) +- Use `>=` / `<` for the Python boundary; avoid `>` / `<=` so the boundary version lands on exactly one side. **This rule is about the `python_version` marker, not the version specifier** — `boto3<=2` and `cheroot<12` are correct as written. +- One space after the `;`, none before: `pkg>=1,<2; python_version >= "3.9"`. +- The old-side line always carries an explicit upper bound (the floor-jump version). +- The marker set must be **exhaustive and mutually exclusive** across the matrix. The newest line ends open-ended (`>= "X.Y"`), never a bare `==` that leaves future Pythons unmatched. + +## Deriving the versions to pin + +You need two numbers: the **floor** (which Python the new release requires) and the old-side **ceiling** (the first release that raised that floor). + +1. **Floor.** Read the metadata for the _exact target version_ at `https://pypi.org/pypi///json`. The `info.requires_python` field gives the new minimum (e.g. `">=3.9"`). A `null` there means the release declares no floor. + +2. **Ceiling.** Walk the release history at `https://pypi.org/pypi//json` and find the **first version that raised the floor above the oldest matrix Python**. The old-side ceiling is `< `. For example, if a package jumped to `>=3.9` at version **4.0.0**, the old-side cap is `<4` even if the target is `4.2.0` (pinning `<4.2.0` would wrongly admit 4.0.0–4.1.x, which are also 3.9-only). + +Why an explicit ceiling instead of trusting pip to filter by `Requires-Python`? Because that filtering only holds if every future release keeps its metadata correct; a single mis-tagged release would silently float onto an untested interpreter. An explicit ceiling makes the intent self-documenting and robust. + +`tracerite` is the cautionary case: its releases after 1.1.2 break on Python `<= 3.8`, yet those releases publish **no `requires_python` metadata at all** (verify: `https://pypi.org/pypi/tracerite/1.1.3/json` shows `requires_python: null`). So pip filtering offers zero protection on the old interpreters, and the explicit `tracerite<1.1.2` ceiling is load-bearing, not decorative. + +**If you arrived here from a red CI job:** the failing _install_ log is ground truth. It names the interpreter that failed and the versions pip was actually offered, e.g.: + +``` +ERROR: Ignored the following versions that require a different python version: 4.2.0 Requires-Python >=3.9 +ERROR: Could not find a version that satisfies the requirement falcon>=4.2.0 (from versions: ..., 3.1.3) +``` + +Cross-check the PyPI value against that log so you are never guessing. (If instead the failure is a real test failure, or hits _every_ Python version, this pattern does not apply, so investigate the bump normally.) + +## One harder shape: a coupled companion dependency + +Sometimes a package drags in another distribution with loose or wildcard versions that you must co-pin on the same boundary. `Sanic` imports `tracerite` with wildcard versions, so `tracerite` is pinned right beside it: + +``` +# sanic +# Note: Sanic pulls in tracerite via a wildcard version, so tracerite is co-pinned here. +# Note: tracerite > 1.1.2 is incompatible with Python <= 3.8 and ships no requires_python, so an explicit ceiling is required. +tracerite<1.1.2; python_version < "3.9" +sanic>=21,<24; python_version < "3.9" +sanic>=25.3.0,<26; python_version >= "3.9" +``` + +The split boundary here (3.9) is driven by `tracerite`, **not** by Sanic itself — Sanic 25.3 supports Python 3.8 (`requires_python: >=3.8`). Python 3.8 is kept on old Sanic only because tracerite breaks there. When a companion transitive dependency is the thing that breaks, pin it explicitly rather than hoping the parent's resolver picks a compatible version, and keep it in the same section so the coupling stays visible. + +## Collapse when a Python is dropped + +Marker splits are maintenance cost, so remove them when they stop earning their keep. When a Python version is dropped from the CI matrix (and from `requires-python` / the classifiers), collapse any split whose only reason was that version back into a single unmarked line, and delete the trailing `;`. For example, if 3.7 is dropped, the `Django>=3.2,<4; python_version < "3.8"` line has no interpreter left to serve, and the section collapses to a single `Django` line. A leaner file is easier for both humans and Dependabot to reason about. + +## What to leave alone + +- **Do not touch `requires-python` or the CI matrix.** Keeping 3.7 working on old dependency versions is the entire point; changing the floor is a separate, deliberate decision. +- **Do not add runtime dependencies to `pyproject.toml`.** The core package depends only on `slack_sdk` (see the "Single Runtime Dependency Rule" in `AGENTS.md`); everything else belongs in `requirements/*.txt`. +- **Prefer markers over a Dependabot `ignore`.** An `ignore` rule freezes newer Pythons on the old version too, and hides the version knowledge in config. Reserve `ignore` for the rare dep that must stay pinned everywhere for reproducible output. diff --git a/AGENTS.md b/AGENTS.md index 005f6eeda..eae86f330 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -214,6 +214,8 @@ The core package has a **single required runtime dependency**: `slack_sdk` (defi When adding a new dependency: add it to the appropriate `requirements/*.txt` file with version constraints, never to `pyproject.toml` `dependencies` (unless it's a core runtime dep, which is very rare). +Before adding, bumping, pinning, or reviewing any dependency in `requirements/*.txt` -- whether a Dependabot PR or a manual edit -- follow the `managing-dependencies` skill in `.claude/skills/`. It defines the layout and `python_version` marker conventions that keep one set of pins working across the full CPython 3.7--3.14 matrix. + ## Test Organization and CI ### Directory Structure diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index d118106b9..ac70e1fdd 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -1,34 +1,73 @@ # pip install -r requirements/adapter_dev.txt -# NOTE: any of async ones requires pip install -r requirements/async_dev.txt too -# used only under slack_bolt/adapter +# Note: any of the async ones requires pip install -r requirements/async_dev.txt too +# Note: used only under slack_bolt/adapter + +# boto3 boto3<=2 + +# bottle bottle>=0.12,<1 -chalice>=1.28,<1.31; python_version<"3.9" -chalice>=1.32.0,<2; python_version>="3.9" + +# chalice +chalice>=1.28,<1.31; python_version < "3.9" +chalice>=1.32.0,<2; python_version >= "3.9" + +# cheroot cheroot<12 + +# CherryPy CherryPy>=18.10.0,<19 -Django>=3.2,<4; python_version<"3.8" -Django>=4.2.30,<6; python_version>="3.8" -falcon>=2,<4; python_version<"3.9" -falcon>=4.2.0,<5; python_version>="3.9" -fastapi>=0.70.0,<1; python_version<"3.9" -fastapi>=0.128.8,<1; python_version>="3.9" -Flask>=1,<4; python_version<"3.9" -Flask>=3.1.3,<4; python_version>="3.9" -Werkzeug>=2,<3; python_version<"3.9" -Werkzeug>=3.1.8,<4; python_version>="3.9" + +# Django +# Note: Django 4.2.30 requires Python >=3.8; 3.7 stays on the 3.2 line. +Django>=3.2,<4; python_version < "3.8" +Django>=4.2.30,<6; python_version >= "3.8" + +# falcon +# Note: falcon 4.2.0 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +falcon>=2,<4; python_version < "3.9" +falcon>=4.2.0,<5; python_version >= "3.9" + +# fastapi +# Note: fastapi 0.128.8 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +fastapi>=0.70.0,<1; python_version < "3.9" +fastapi>=0.128.8,<1; python_version >= "3.9" + +# Flask +# Note: Flask 3.1.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +Flask>=1,<4; python_version < "3.9" +Flask>=3.1.3,<4; python_version >= "3.9" + +# Werkzeug +# Note: Werkzeug 3.1.8 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +Werkzeug>=2,<3; python_version < "3.9" +Werkzeug>=3.1.8,<4; python_version >= "3.9" + +# pyramid pyramid>=1,<3 -setuptools<82 # Pinned: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 - -# Sanic and its dependencies -# Note: Sanic imports tracerite with wild card versions -tracerite<1.1.2; python_version<="3.8" # older versions of python are not compatible with tracerite>1.1.2 -sanic>=21,<24; python_version<="3.8" -sanic>=25.3.0,<26; python_version>"3.8" - -starlette>=0.19.1,<0.45; python_version<"3.9" -starlette>=0.49.3,<1; python_version>="3.9" -tornado>=6.2,<7; python_version<"3.9" -tornado>=6.5.6,<7; python_version>="3.9" -websocket_client>=1.2.3,<1.9; python_version<"3.9" # Socket Mode 3rd party implementation -websocket_client>=1.9.0,<2; python_version>="3.9" # Socket Mode 3rd party implementation + +# setuptools +# Note: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 +setuptools<82 + +# sanic +# Note: Sanic pulls in tracerite via a wildcard version, so tracerite is co-pinned here. +# Note: tracerite > 1.1.2 is incompatible with Python <= 3.8 and ships no requires_python, so an explicit ceiling is required. +tracerite<1.1.2; python_version < "3.9" +sanic>=21,<24; python_version < "3.9" +sanic>=25.3.0,<26; python_version >= "3.9" + +# starlette +# Note: starlette 0.49.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +starlette>=0.19.1,<0.45; python_version < "3.9" +starlette>=0.49.3,<1; python_version >= "3.9" + +# tornado +# Note: tornado 6.5.6 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +tornado>=6.2,<7; python_version < "3.9" +tornado>=6.5.6,<7; python_version >= "3.9" + +# websocket_client +# Note: Socket Mode 3rd party implementation +websocket_client>=1.2.3,<1.9; python_version < "3.9" +websocket_client>=1.9.0,<2; python_version >= "3.9" diff --git a/requirements/async_dev.txt b/requirements/async_dev.txt index a40433441..32dff305b 100644 --- a/requirements/async_dev.txt +++ b/requirements/async_dev.txt @@ -1,4 +1,9 @@ # pip install -r requirements/async_dev.txt -aiohttp>=3,<4; python_version<"3.9" -aiohttp>=3.13.5,<4; python_version>="3.9" + +# aiohttp +# Note: aiohttp 3.13.5 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +aiohttp>=3,<4; python_version < "3.9" +aiohttp>=3.13.5,<4; python_version >= "3.9" + +# websockets websockets<17 diff --git a/requirements/dev_tools.txt b/requirements/dev_tools.txt index dd13bd614..0b826cf41 100644 --- a/requirements/dev_tools.txt +++ b/requirements/dev_tools.txt @@ -1,3 +1,10 @@ +# pip install -r requirements/dev_tools.txt + +# mypy mypy==1.19.1 + +# flake8 flake8==7.3.0 + +# black black==26.3.1 diff --git a/requirements/test.txt b/requirements/test.txt index 020e2f41a..af895692b 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,3 +1,8 @@ # pip install -r requirements/test.txt + +# pytest pytest<9.2 -pytest-cov>=7.1.0,<8; python_version>="3.14" # only needed to evaluate coverage on the latest supported python version + +# pytest-cov +# Note: only needed to evaluate coverage on the latest supported python version +pytest-cov>=7.1.0,<8; python_version >= "3.14" diff --git a/requirements/test_adapter.txt b/requirements/test_adapter.txt index ecb3741f2..0757e7bce 100644 --- a/requirements/test_adapter.txt +++ b/requirements/test_adapter.txt @@ -1,4 +1,12 @@ # pip install -r requirements/test_adapter.txt -moto>=3,<6 # For AWS tests -boddle>=0.2.9,<0.3 # For Bottle app tests + +# moto +# Note: for AWS tests +moto>=3,<6 + +# boddle +# Note: for Bottle app tests +boddle>=0.2.9,<0.3 + +# sanic-testing sanic-testing>=0.7 diff --git a/requirements/test_async.txt b/requirements/test_async.txt index 8fa6c6806..5dc51a201 100644 --- a/requirements/test_async.txt +++ b/requirements/test_async.txt @@ -1,6 +1,11 @@ # pip install -r requirements/test_async.txt -r test.txt -r async_dev.txt -asgiref>=3.7.2,<3.8; python_version<"3.9" -asgiref>=3.11.1,<4; python_version>="3.9" -pytest-asyncio<2; + +# asgiref +# Note: asgiref 3.11.1 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. +asgiref>=3.7.2,<3.8; python_version < "3.9" +asgiref>=3.11.1,<4; python_version >= "3.9" + +# pytest-asyncio +pytest-asyncio<2 From 58b3de50436ec73cefbc715e521749e0d3718638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:29:28 -0700 Subject: [PATCH 267/282] chore(deps): update moto requirement from <6,>=3 to >=5.2.2,<6 (#1546) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/test_adapter.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/requirements/test_adapter.txt b/requirements/test_adapter.txt index 0757e7bce..44af631f4 100644 --- a/requirements/test_adapter.txt +++ b/requirements/test_adapter.txt @@ -2,7 +2,11 @@ # moto # Note: for AWS tests -moto>=3,<6 +# Note: moto drops old Pythons across the 5.x line — 5.0.0 requires >=3.8, 5.1.0 >=3.9, 5.2.0 >=3.10; older interpreters stay on the last compatible release. +moto>=3,<5; python_version < "3.8" +moto>=3,<5.1; python_version >= "3.8" and python_version < "3.9" +moto>=3,<5.2; python_version >= "3.9" and python_version < "3.10" +moto>=5.2.2,<6; python_version >= "3.10" # boddle # Note: for Bottle app tests From abac1dd816fe263b18e9caa8f9db071709f26e24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:58:58 -0700 Subject: [PATCH 268/282] chore(deps-dev): update django requirement from <6,>=4.2.30 to >=5.2.15,<6 (#1539) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index ac70e1fdd..f313dc945 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -19,9 +19,10 @@ cheroot<12 CherryPy>=18.10.0,<19 # Django -# Note: Django 4.2.30 requires Python >=3.8; 3.7 stays on the 3.2 line. +# Note: Django 5.x requires Python >=3.10 and 4.x requires >=3.8, so 3.8/3.9 stay on the 4.2 LTS line and 3.7 stays on the 3.2 line. Django>=3.2,<4; python_version < "3.8" -Django>=4.2.30,<6; python_version >= "3.8" +Django>=4.2.30,<5; python_version >= "3.8" and python_version < "3.10" +Django>=5.2.15,<6; python_version >= "3.10" # falcon # Note: falcon 4.2.0 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. From be299e7671c4df699697b06dfad3eeb60fb5e0ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 12:37:17 -0700 Subject: [PATCH 269/282] chore(deps-dev): update pyramid requirement from <3,>=1 to >=2.1,<3 (#1538) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index f313dc945..959ee8823 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -45,7 +45,9 @@ Werkzeug>=2,<3; python_version < "3.9" Werkzeug>=3.1.8,<4; python_version >= "3.9" # pyramid -pyramid>=1,<3 +# Note: pyramid 2.1 requires Python >=3.10; 3.7/3.8/3.9 stay on the last 2.0.x release. +pyramid>=1,<2.1; python_version < "3.10" +pyramid>=2.1,<3; python_version >= "3.10" # setuptools # Note: Pyramid depends on pkg_resources (deprecated in setuptools 67.5.0, removed in 82+). See: https://github.com/Pylons/pyramid/issues/3731 From 5a0cfb80a16b8789ac03c8babe8fa79fe28ca8c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 13:27:46 -0700 Subject: [PATCH 270/282] chore(deps): update sanic-testing requirement from >=0.7 to >=24.6.0 (#1547) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/test_adapter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/test_adapter.txt b/requirements/test_adapter.txt index 44af631f4..702856aac 100644 --- a/requirements/test_adapter.txt +++ b/requirements/test_adapter.txt @@ -13,4 +13,4 @@ moto>=5.2.2,<6; python_version >= "3.10" boddle>=0.2.9,<0.3 # sanic-testing -sanic-testing>=0.7 +sanic-testing>=24.6.0 From a2f21ae4b1d1d63a6a5dc533aa53a8c4badd103d Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 20 Jul 2026 12:48:40 -0400 Subject: [PATCH 271/282] ci: do not fail CI when Codecov test-results upload errors (#1552) Co-authored-by: Claude --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 89671a50c..a250564f4 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -129,7 +129,7 @@ jobs: uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: directory: ./reports/ - fail_ci_if_error: true + fail_ci_if_error: false flags: ${{ matrix.python-version }} report_type: test_results token: ${{ secrets.CODECOV_TOKEN }} From 097e8c421023aac845d680226b5b9dd60e5944d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 22:15:54 +0000 Subject: [PATCH 272/282] chore(deps): bump pypa/gh-action-pypi-publish from 1.14.0 to 1.14.2 (#1554) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 624243bf6..2927afa2c 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -60,7 +60,7 @@ jobs: - name: Publish release distributions to test.pypi.org # Using OIDC for PyPI publishing (no API tokens needed) # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: repository-url: https://test.pypi.org/legacy/ @@ -84,4 +84,4 @@ jobs: - name: Publish release distributions to pypi.org # Using OIDC for PyPI publishing (no API tokens needed) # See: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 From 26a3072d3d94a56125f8411eed58172f9b072c67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 22:20:50 +0000 Subject: [PATCH 273/282] chore(deps): bump actions/checkout from 7.0.0 to 7.0.1 (#1559) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index a250564f4..df435f86d 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -20,7 +20,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -37,7 +37,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} @@ -77,7 +77,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} @@ -144,7 +144,7 @@ jobs: env: BOLT_PYTHON_CODECOV_RUNNING: "1" steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 2927afa2c..a3743b990 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.release.tag_name || github.ref }} persist-credentials: false From 8d1c383a3320dd1609f18d28ce45f60d41b9fa37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:14:55 -0700 Subject: [PATCH 274/282] chore(deps): bump actions/setup-python from 6.3.0 to 7.0.0 (#1557) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-build.yml | 8 ++++---- .github/workflows/pypi-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index df435f86d..c62ce61e6 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -24,7 +24,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Run lint verification @@ -41,7 +41,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Install synchronous dependencies @@ -81,7 +81,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python-version }} - name: Install synchronous dependencies @@ -148,7 +148,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.LATEST_SUPPORTED_PY }} - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - name: Install dependencies diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index a3743b990..2d0f7630a 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.x" From c49416b33b002962886d6520559bfe2f29315e31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:59:01 -0700 Subject: [PATCH 275/282] chore(deps-dev): update sanic requirement from <26,>=25.3.0 to >=25.12.1,<26 (#1556) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 959ee8823..3fa8d003a 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -56,9 +56,11 @@ setuptools<82 # sanic # Note: Sanic pulls in tracerite via a wildcard version, so tracerite is co-pinned here. # Note: tracerite > 1.1.2 is incompatible with Python <= 3.8 and ships no requires_python, so an explicit ceiling is required. +# Note: sanic 25.12 requires Python >=3.10; 3.9 stays on the older 25.x line. tracerite<1.1.2; python_version < "3.9" sanic>=21,<24; python_version < "3.9" -sanic>=25.3.0,<26; python_version >= "3.9" +sanic>=25.3.0,<25.12.0; python_version >= "3.9" and python_version < "3.10" +sanic>=25.12.1,<26; python_version >= "3.10" # starlette # Note: starlette 0.49.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. From 62e7ea4274fc60e8a1c7142b1e50992ae17bb009 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:03:11 +0000 Subject: [PATCH 276/282] chore(deps-dev): update bottle requirement from <1,>=0.12 to >=0.13.4,<1 (#1555) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/adapter_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 3fa8d003a..4fb4cf774 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -6,7 +6,7 @@ boto3<=2 # bottle -bottle>=0.12,<1 +bottle>=0.13.4,<1 # chalice chalice>=1.28,<1.31; python_version < "3.9" From 8d279b7ebb0bcb73bb57514d6fa3b433331a669f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:12:14 +0000 Subject: [PATCH 277/282] chore(deps): bump slackapi/slack-github-action from 3.0.3 to 4.0.0 (#1561) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index c62ce61e6..dd2119b8c 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -179,7 +179,7 @@ jobs: if: ${{ !success() && github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' }} steps: - name: Send notifications of failing tests - uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 with: errors: true webhook: ${{ secrets.SLACK_REGRESSION_FAILURES_WEBHOOK_URL }} From a9153faf1a11debf1e5c14ffa393534145ee6a63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:17:18 +0000 Subject: [PATCH 278/282] chore(deps): bump actions/stale from 10.3.0 to 11.0.0 (#1553) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/triage-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index 9d99c40da..adad546b8 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -16,7 +16,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 + - uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0 with: days-before-issue-stale: 30 days-before-issue-close: 10 From cfb983033c62fcd1f7eace284275d77d49926dc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:36:32 +0000 Subject: [PATCH 279/282] chore(deps-dev): bump mypy from 1.19.1 to 2.3.0 (#1560) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin --- requirements/dev_tools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/dev_tools.txt b/requirements/dev_tools.txt index 0b826cf41..59e4cdd21 100644 --- a/requirements/dev_tools.txt +++ b/requirements/dev_tools.txt @@ -1,7 +1,7 @@ # pip install -r requirements/dev_tools.txt # mypy -mypy==1.19.1 +mypy==2.3.0 # flake8 flake8==7.3.0 From 59ec0443fa9bf90eda57cee6108049e302c81a8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:47:02 +0000 Subject: [PATCH 280/282] chore(deps-dev): update fastapi requirement from <1,>=0.128.8 to >=0.141.1,<1 (#1562) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index 4fb4cf774..c5cd52ca7 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -30,9 +30,9 @@ falcon>=2,<4; python_version < "3.9" falcon>=4.2.0,<5; python_version >= "3.9" # fastapi -# Note: fastapi 0.128.8 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. fastapi>=0.70.0,<1; python_version < "3.9" -fastapi>=0.128.8,<1; python_version >= "3.9" +fastapi>=0.128.8,<0.129.0; python_version >= "3.9" and python_version < "3.10" +fastapi>=0.141.1,<1; python_version >= "3.10" # Flask # Note: Flask 3.1.3 requires Python >=3.9; 3.7/3.8 stay on the older pinned release. From 2572efb6550b20cb0bb4162e5ffd59223579b68d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:51:22 +0000 Subject: [PATCH 281/282] chore(deps-dev): update chalice requirement from <2,>=1.32.0 to >=1.33.0,<2 (#1558) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Bergamin Co-authored-by: Claude --- requirements/adapter_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/adapter_dev.txt b/requirements/adapter_dev.txt index c5cd52ca7..34a96a911 100644 --- a/requirements/adapter_dev.txt +++ b/requirements/adapter_dev.txt @@ -10,7 +10,8 @@ bottle>=0.13.4,<1 # chalice chalice>=1.28,<1.31; python_version < "3.9" -chalice>=1.32.0,<2; python_version >= "3.9" +chalice>=1.32.0,<1.33; python_version >= "3.9" and python_version < "3.10" +chalice>=1.33.0,<2; python_version >= "3.10" # cheroot cheroot<12 From a70d2475d184acfe74ef2f7619a37ef6f81951a0 Mon Sep 17 00:00:00 2001 From: Tracy Rericha Date: Mon, 10 Aug 2026 13:50:28 -0400 Subject: [PATCH 282/282] docs request --- docs/english/concepts/authorization.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/english/concepts/authorization.md b/docs/english/concepts/authorization.md index f6a258491..9d0086ca7 100644 --- a/docs/english/concepts/authorization.md +++ b/docs/english/concepts/authorization.md @@ -2,9 +2,10 @@ Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack request. -Apps installed on a single workspace can simply pass their bot token into the `App` constructor using the `token` parameter. However, if your app will be installed on multiple workspaces, you have two options. The easier option is to use the built-in OAuth support. This will handle setting up OAuth routes and verifying state. Read the section on [authenticating with OAuth](/tools/bolt-python/concepts/authenticating-oauth) for details. +Apps installed on a single workspace can pass their bot token into the `App` constructor using the `token` parameter. However, if your app will be installed on multiple workspaces, you have two options: -For a more custom solution, you can set the `authorize` parameter to a function upon `App` instantiation. The `authorize` function should return [an instance of `AuthorizeResult`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/authorization/authorize_result.py), which contains information about who and where the request is coming from. +* Use the built-in OAuth support. This will handle setting up OAuth routes and verifying state. See [authenticating with OAuth](/tools/bolt-python/concepts/authenticating-oauth) for more details. +* Set the `authorize` parameter to a function upon `App` instantiation. The `authorize` function should return [an instance of `AuthorizeResult`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/authorization/authorize_result.py), which contains information about who and where the request is coming from. `AuthorizeResult` should have a few specific properties, all of type `str`: - Either **`bot_token`** (xoxb) *or* **`user_token`** (xoxp) are **required**. Most apps will use `bot_token` by default. Passing a token allows built-in functions (like `say()`) to work. @@ -62,3 +63,11 @@ app = App( authorize=authorize ) ``` + +## Handling failed token lookups {#handling-failed-token-lookups} + +In the event that you receive events from disconnected teams, make sure to gracefully drop these unauthorized payloads by returning `None` to silently drop the payload as follows. + +In the custom `authorize` callback, if a token lookup fails due to an `unknown team_id`, you should return `None` rather than raising an exception (such as `BoltUnauthorizedError`). Bolt's authorization middleware will recognize the `None` response as an authorization failure and immediately halt execution, silently dropping the payload without generating server error logs. + +Additionally, the `user_facing_authorize_error_message` parameter strictly controls the ephemeral message sent back to the end user via the Slack UI. It does not suppress internal server logs or exceptions. To achieve both the desired UI behavior and clean server logs, pair this parameter with returning `None` in your authorize function.
  • kuHci1T>%df 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ÁL-1d-(Q|-QVD5f?wX!S;g7ykz;DfF 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�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 diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico deleted file mode 100644 index e6e9a4aa71ffae29bbfaeacc2bb431272df45cc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24499 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 diff --git a/docs/static/img/slack-logo-on-white.png b/docs/static/img/slack-logo-on-white.png deleted file mode 100644 index 2a73996c6c402db042920a47e65a82be85e25e1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25811 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 Q{izOvV`#l%UB?^$2eLeXjsO4v diff --git a/docs/static/img/slack-logo.svg b/docs/static/img/slack-logo.svg deleted file mode 100644 index fb55f7245..000000000 --- a/docs/static/img/slack-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/examples/getting_started/app.py b/examples/getting_started/app.py index 22cdf5f31..aa5223d51 100644 --- a/examples/getting_started/app.py +++ b/examples/getting_started/app.py @@ -10,7 +10,7 @@ # Listens to incoming messages that contain "hello" # To learn available listener method arguments, -# visit https://slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html +# visit https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered diff --git a/scripts/generate_api_docs.sh b/scripts/generate_api_docs.sh index 68988f428..459476122 100755 --- a/scripts/generate_api_docs.sh +++ b/scripts/generate_api_docs.sh @@ -5,6 +5,6 @@ script_dir=`dirname $0` cd ${script_dir}/.. pip install -U pdoc3 -rm -rf docs/static/api-docs -pdoc slack_bolt --html -o docs/static/api-docs -open docs/static/api-docs/slack_bolt/index.html +rm -rf docs/reference +pdoc reference --html -o docs +open docs/reference/index.html From 974816eee7966adab9e14d890e0908922fa3a77a Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 13 Aug 2025 17:28:46 -0400 Subject: [PATCH 133/282] fix: make the function handler timeout 5 seconds (#1348) Co-authored-by: Eden Zimbelman --- slack_bolt/app/app.py | 6 ++- slack_bolt/app/async_app.py | 6 ++- slack_bolt/listener/async_listener.py | 4 ++ slack_bolt/listener/asyncio_runner.py | 2 +- slack_bolt/listener/custom_listener.py | 3 ++ slack_bolt/listener/listener.py | 1 + slack_bolt/listener/thread_runner.py | 2 +- tests/scenario_tests/test_function.py | 35 ++++++++++++++++- tests/scenario_tests_async/test_function.py | 43 ++++++++++++++++++++- 9 files changed, 95 insertions(+), 7 deletions(-) diff --git a/slack_bolt/app/app.py b/slack_bolt/app/app.py index c117740a1..86909ed18 100644 --- a/slack_bolt/app/app.py +++ b/slack_bolt/app/app.py @@ -946,7 +946,9 @@ def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener( + functions, primary_matcher, matchers, middleware, auto_acknowledge, acknowledgement_timeout=5 + ) return __call__ @@ -1422,6 +1424,7 @@ def _register_listener( matchers: Optional[Sequence[Callable[..., bool]]], middleware: Optional[Sequence[Union[Callable, Middleware]]], auto_acknowledgement: bool = False, + acknowledgement_timeout: int = 3, ) -> Optional[Callable[..., Optional[BoltResponse]]]: value_to_return = None if not isinstance(functions, list): @@ -1452,6 +1455,7 @@ def _register_listener( matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, + acknowledgement_timeout=acknowledgement_timeout, base_logger=self._base_logger, ) ) diff --git a/slack_bolt/app/async_app.py b/slack_bolt/app/async_app.py index c04326291..294fb8b0c 100644 --- a/slack_bolt/app/async_app.py +++ b/slack_bolt/app/async_app.py @@ -976,7 +976,9 @@ def __call__(*args, **kwargs): primary_matcher = builtin_matchers.function_executed( callback_id=callback_id, base_logger=self._base_logger, asyncio=True ) - return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge) + return self._register_listener( + functions, primary_matcher, matchers, middleware, auto_acknowledge, acknowledgement_timeout=5 + ) return __call__ @@ -1456,6 +1458,7 @@ def _register_listener( matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]], middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]], auto_acknowledgement: bool = False, + acknowledgement_timeout: int = 3, ) -> Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]: value_to_return = None if not isinstance(functions, list): @@ -1491,6 +1494,7 @@ def _register_listener( matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, + acknowledgement_timeout=acknowledgement_timeout, base_logger=self._base_logger, ) ) diff --git a/slack_bolt/listener/async_listener.py b/slack_bolt/listener/async_listener.py index c8758daf2..ca069b097 100644 --- a/slack_bolt/listener/async_listener.py +++ b/slack_bolt/listener/async_listener.py @@ -15,6 +15,7 @@ class AsyncListener(metaclass=ABCMeta): ack_function: Callable[..., Awaitable[BoltResponse]] lazy_functions: Sequence[Callable[..., Awaitable[None]]] auto_acknowledgement: bool + acknowledgement_timeout: int async def async_matches( self, @@ -87,6 +88,7 @@ class AsyncCustomListener(AsyncListener): matchers: Sequence[AsyncListenerMatcher] middleware: Sequence[AsyncMiddleware] auto_acknowledgement: bool + acknowledgement_timeout: int arg_names: MutableSequence[str] logger: Logger @@ -99,6 +101,7 @@ def __init__( matchers: Sequence[AsyncListenerMatcher], middleware: Sequence[AsyncMiddleware], auto_acknowledgement: bool = False, + acknowledgement_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -107,6 +110,7 @@ def __init__( self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement + self.acknowledgement_timeout = acknowledgement_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) diff --git a/slack_bolt/listener/asyncio_runner.py b/slack_bolt/listener/asyncio_runner.py index 56dc29cc1..98d3bf4f8 100644 --- a/slack_bolt/listener/asyncio_runner.py +++ b/slack_bolt/listener/asyncio_runner.py @@ -149,7 +149,7 @@ async def run_ack_function_asynchronously( self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= 3: + while ack.response is None and time.time() - starting_time <= listener.acknowledgement_timeout: await asyncio.sleep(0.01) if response is None and ack.response is None: diff --git a/slack_bolt/listener/custom_listener.py b/slack_bolt/listener/custom_listener.py index b785dab6d..e2977effa 100644 --- a/slack_bolt/listener/custom_listener.py +++ b/slack_bolt/listener/custom_listener.py @@ -18,6 +18,7 @@ class CustomListener(Listener): matchers: Sequence[ListenerMatcher] middleware: Sequence[Middleware] auto_acknowledgement: bool + acknowledgement_timeout: int = 3 arg_names: MutableSequence[str] logger: Logger @@ -30,6 +31,7 @@ def __init__( matchers: Sequence[ListenerMatcher], middleware: Sequence[Middleware], auto_acknowledgement: bool = False, + acknowledgement_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name @@ -38,6 +40,7 @@ def __init__( self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement + self.acknowledgement_timeout = acknowledgement_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) diff --git a/slack_bolt/listener/listener.py b/slack_bolt/listener/listener.py index d938935df..51dadae56 100644 --- a/slack_bolt/listener/listener.py +++ b/slack_bolt/listener/listener.py @@ -13,6 +13,7 @@ class Listener(metaclass=ABCMeta): ack_function: Callable[..., BoltResponse] lazy_functions: Sequence[Callable[..., None]] auto_acknowledgement: bool + acknowledgement_timeout: int = 3 def matches( self, diff --git a/slack_bolt/listener/thread_runner.py b/slack_bolt/listener/thread_runner.py index c144daf1d..61e8d6129 100644 --- a/slack_bolt/listener/thread_runner.py +++ b/slack_bolt/listener/thread_runner.py @@ -160,7 +160,7 @@ def run_ack_function_asynchronously(): self._start_lazy_function(lazy_func, request) # await for the completion of ack() in the async listener execution - while ack.response is None and time.time() - starting_time <= 3: + while ack.response is None and time.time() - starting_time <= listener.acknowledgement_timeout: time.sleep(0.01) if response is None and ack.response is None: diff --git a/tests/scenario_tests/test_function.py b/tests/scenario_tests/test_function.py index 00f0efba8..41290de8f 100644 --- a/tests/scenario_tests/test_function.py +++ b/tests/scenario_tests/test_function.py @@ -1,6 +1,7 @@ import json import time import pytest +from unittest.mock import Mock from slack_sdk.signature import SignatureVerifier from slack_sdk.web import WebClient @@ -51,6 +52,10 @@ def build_request_from_body(self, message_body: dict) -> BoltRequest: timestamp, body = str(int(time.time())), json.dumps(message_body) return BoltRequest(body=body, headers=self.build_headers(timestamp, body)) + def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock: Mock, sleep_mock: Mock): + monkeypatch.setattr(time, "time", time_mock) + monkeypatch.setattr(time, "sleep", sleep_mock) + def test_valid_callback_id_success(self): app = App( client=self.web_client, @@ -124,7 +129,7 @@ def test_auto_acknowledge_false_with_acknowledging(self): assert response.status == 200 assert_auth_test_count(self, 1) - def test_auto_acknowledge_false_without_acknowledging(self, caplog): + def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkeypatch): app = App( client=self.web_client, signing_secret=self.signing_secret, @@ -132,12 +137,40 @@ def test_auto_acknowledge_false_without_acknowledging(self, caplog): app.function("reverse", auto_acknowledge=False)(just_no_ack) request = self.build_request_from_body(function_body) + self.setup_time_mocks( + monkeypatch=monkeypatch, + time_mock=Mock(side_effect=[current_time for current_time in range(100)]), + sleep_mock=Mock(), + ) response = app.dispatch(request) assert response.status == 404 assert_auth_test_count(self, 1) assert f"WARNING {just_no_ack.__name__} didn't call ack()" in caplog.text + def test_function_handler_timeout(self, monkeypatch): + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse", auto_acknowledge=False)(just_no_ack) + request = self.build_request_from_body(function_body) + + sleep_mock = Mock() + self.setup_time_mocks( + monkeypatch=monkeypatch, + time_mock=Mock(side_effect=[current_time for current_time in range(100)]), + sleep_mock=sleep_mock, + ) + + response = app.dispatch(request) + + assert response.status == 404 + assert_auth_test_count(self, 1) + assert ( + sleep_mock.call_count == 5 + ), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times" + function_body = { "token": "verification_token", diff --git a/tests/scenario_tests_async/test_function.py b/tests/scenario_tests_async/test_function.py index a2c10950c..fc1299e55 100644 --- a/tests/scenario_tests_async/test_function.py +++ b/tests/scenario_tests_async/test_function.py @@ -3,6 +3,7 @@ import time import pytest +from unittest.mock import Mock, MagicMock from slack_sdk.signature import SignatureVerifier from slack_sdk.web.async_client import AsyncWebClient @@ -17,6 +18,10 @@ from tests.utils import remove_os_env_temporarily, restore_os_env +async def fake_sleep(seconds): + pass + + class TestAsyncFunction: signing_secret = "secret" valid_token = "xoxb-valid" @@ -56,6 +61,10 @@ def build_request_from_body(self, message_body: dict) -> AsyncBoltRequest: timestamp, body = str(int(time.time())), json.dumps(message_body) return AsyncBoltRequest(body=body, headers=self.build_headers(timestamp, body)) + def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock: Mock, sleep_mock: MagicMock): + monkeypatch.setattr(time, "time", time_mock) + monkeypatch.setattr(asyncio, "sleep", sleep_mock) + @pytest.mark.asyncio async def test_mock_server_is_running(self): resp = await self.web_client.api_test() @@ -130,19 +139,49 @@ async def test_auto_acknowledge_false_with_acknowledging(self): await assert_auth_test_count_async(self, 1) @pytest.mark.asyncio - async def test_auto_acknowledge_false_without_acknowledging(self, caplog): + async def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkeypatch): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) app.function("reverse", auto_acknowledge=False)(just_no_ack) - request = self.build_request_from_body(function_body) + + self.setup_time_mocks( + monkeypatch=monkeypatch, + time_mock=Mock(side_effect=[current_time for current_time in range(100)]), + sleep_mock=MagicMock(side_effect=fake_sleep), + ) + response = await app.async_dispatch(request) assert response.status == 404 await assert_auth_test_count_async(self, 1) assert f"WARNING {just_no_ack.__name__} didn't call ack()" in caplog.text + @pytest.mark.asyncio + async def test_function_handler_timeout(self, monkeypatch): + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + ) + app.function("reverse", auto_acknowledge=False)(just_no_ack) + request = self.build_request_from_body(function_body) + + sleep_mock = MagicMock(side_effect=fake_sleep) + self.setup_time_mocks( + monkeypatch=monkeypatch, + time_mock=Mock(side_effect=[current_time for current_time in range(100)]), + sleep_mock=sleep_mock, + ) + + response = await app.async_dispatch(request) + + assert response.status == 404 + await assert_auth_test_count_async(self, 1) + assert ( + sleep_mock.call_count == 5 + ), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times" + function_body = { "token": "verification_token", From 76f70278403f1b0e283d2e9a7276a691d5e91c6f Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Fri, 22 Aug 2025 12:03:31 -0700 Subject: [PATCH 134/282] Docs: adds updated bolt-py image to README (#1352) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6b6a536c..10a44a0e5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -